Running multiple containers on a budget VPS (1–2 GB RAM) requires careful resource planning. Docker Compose makes orchestration straightforward, but default configurations can quickly exhaust memory. This guide shows how to deploy a full multi-container stack — Nginx reverse proxy, PHP-FPM, PostgreSQL, and Redis — on a VPS with only 1 GB RAM, using Docker Compose resource limits, health checks, and Alpine-based images.
Understanding why resource efficiency matters is the first step. Check our VPS comparison table for plans that balance RAM and cost for containerized workloads.
Why Docker Compose on a Budget VPS?
Docker Compose allows you to define and run multi-container applications with a single docker-compose.yml file. On a budget VPS, the challenge is fitting all services within limited RAM without swapping. Key strategies include:
- Using Alpine-based images (20–50 MB vs. 200–600 MB for Debian/Ubuntu base)
- Setting explicit
mem_limitandmem_reservationper service - Disabling unnecessary services (e.g., PHP opcache file cache if using Redis)
- Sharing volumes and networks to reduce overhead
Step 1: Install Docker and Docker Compose
On a fresh Ubuntu 22.04 VPS:
sudo apt update
sudo apt install -y docker.io docker-compose-v2
sudo systemctl enable --now docker
docker --version
docker compose version
Step 2: Define the Minimal Stack
Here’s a docker-compose.yml configured for a 1 GB RAM VPS:
version: '3.8'
services:
nginx:
image: nginx:alpine
mem_limit: 128m
mem_reservation: 64m
ports:
- "80:80"
volumes:
- ./app:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
php:
condition: service_healthy
networks:
- app-net
php:
image: php:8.3-fpm-alpine
mem_limit: 256m
mem_reservation: 128m
volumes:
- ./app:/var/www/html
environment:
- PHP_MEMORY_LIMIT=128M
- PHP_MAX_EXECUTION_TIME=30
healthcheck:
test: ["CMD", "php-fpm", "-t"]
interval: 10s
timeout: 5s
retries: 3
networks:
- app-net
postgres:
image: postgres:16-alpine
mem_limit: 256m
mem_reservation: 128m
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: changeme
POSTGRES_SHARED_BUFFERS: 64MB
POSTGRES_EFFECTIVE_CACHE_SIZE: 192MB
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-net
redis:
image: redis:7-alpine
mem_limit: 64m
mem_reservation: 32m
command: redis-server --maxmemory 48mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
networks:
- app-net
volumes:
pgdata:
networks:
app-net:
driver: bridge
Total reserved memory: 384 MB | Total limit: 704 MB. This leaves ~300 MB for the kernel, Docker daemon, and headroom — well within a 1 GB VPS.
Step 3: Memory Budget Breakdown
| Service | Image Size | Mem Limit | Mem Reservation | CPU Limit (optional) |
|---|---|---|---|---|
| Nginx (Alpine) | 23 MB | 128 MB | 64 MB | 0.5 |
| PHP-FPM 8.3 (Alpine) | 85 MB | 256 MB | 128 MB | 1.0 |
| PostgreSQL 16 (Alpine) | 157 MB | 256 MB | 128 MB | 1.0 |
| Redis 7 (Alpine) | 32 MB | 64 MB | 32 MB | 0.5 |
| Total | 297 MB | 704 MB | 352 MB | 3.0 |
Compare this to a default Ubuntu-based stack: Nginx (187 MB), PHP (325 MB), PostgreSQL (378 MB), Redis (118 MB) = over 1 GB before any application data. Alpine images save 60–70% of baseline memory.
Step 4: Deploy and Verify
docker compose up -d
docker compose ps
docker stats --no-stream
The docker stats command shows real-time memory usage per container. With the limits above, the entire stack should idle around 400–500 MB. If you’re close to your VPS’s RAM ceiling, increase swappiness to 10 (as covered in our swap optimization guide) and check our performance benchmarks for VPS plans with faster storage to reduce swap latency.
Step 5: Production Readiness
- Persistent storage: Named volumes (like
pgdata) survive container rebuilds. Always use them for databases. - Health checks: The
depends_on: condition: service_healthyensures services start in the correct order. - Restart policy: Add
restart: unless-stoppedto each service for automatic recovery after crashes or VPS reboots. - Resource limits: Without
mem_limit, a memory leak in one container can starve all others. Always set limits.
For even tighter memory budgets, consider replacing PostgreSQL with SQLite (file-based, zero daemon overhead) or using a single Alpine container with multiple processes via supervisord. However, Docker Compose is the recommended approach for maintainability. See our VPS comparison table for plans optimized for containerized workloads.




Leave a Reply
You must be logged in to post a comment.