Step‑by‑Step Guide to Deploy Multi‑Container Applications with Docker Compose on a Linux VPS
Step‑by‑Step Guide to Deploy Multi‑Container Applications with Docker Compose on a Linux VPS
Running a single‑container app is easy, but production workloads often need several services that must start together, share a network, and persist data. Docker Compose provides a declarative way to define and launch such stacks. This tutorial shows how to install Docker Engine, add Docker Compose, write a docker‑compose.yml for a typical web stack, and configure the VPS to keep the stack running after reboots.
Prerequisites
- A fresh Linux VPS (Ubuntu 22.04 LTS or Debian 12 recommended).
- Root or sudo access.
- Basic familiarity with Linux command line.
- Static IP address or DNS record pointing to the VPS.
1. Install Docker Engine
1.1 Add Docker’s official repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# Import Docker’s GPG key
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Set up the stable repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
1.2 Install the engine packages
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
1.3 Verify the installation
docker version
docker run --rm hello-world
2. Install Docker Compose (v2 plugin)
Docker Compose v2 ships as a Docker CLI plugin, so you only need to download the binary.
DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -Po '"tag_name": "\K.*?(?=")')
sudo curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
# Test the plugin
docker compose version
3. Create a Sample Multi‑Container Stack
We’ll build a simple LEMP stack: Nginx as a reverse proxy, PHP‑FPM for dynamic pages, and MariaDB for persistence.
3.1 Directory layout
mkdir -p ~/lemp/{nginx,php,mariadb}
cd ~/lemp
3.2 Nginx configuration
cat > nginx/default.conf <<'EOF'
server {
listen 80;
server_name _;
root /var/www/html;
index index.php index.html;
location ~ \.php$ {
fastcgi_pass php:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
}
}
EOF
3.3 PHP‑FPM Dockerfile
cat > php/Dockerfile <<'EOF'
FROM php:8.2-fpm
WORKDIR /var/www/html
COPY ./src/ /var/www/html/
EOF
3.4 Sample PHP application
mkdir -p php/src
cat > php/src/index.php <<'EOF'
EOF
3.5 docker‑compose.yml
cat > docker-compose.yml <<'EOF'
version: "3.9"
services:
nginx:
image: nginx:stable-alpine
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- php:/var/www/html
depends_on:
- php
php:
build: ./php
volumes:
- php:/var/www/html
mariadb:
image: mariadb:10.11
environment:
MYSQL_ROOT_PASSWORD: example_root_password
MYSQL_DATABASE: demo
MYSQL_USER: demo_user
MYSQL_PASSWORD: demo_pass
volumes:
- mariadb_data:/var/lib/mysql
volumes:
php:
mariadb_data:
EOF
4. Launch and Test the Stack
docker compose up -d
docker compose ps
Open a browser and navigate to http://YOUR_VPS_IP. You should see the PHP info page, confirming that Nginx, PHP‑FPM, and MariaDB are communicating correctly.
5. Enable Automatic Startup with systemd
To keep the stack alive after a reboot, create a systemd unit that calls Docker Compose.
sudo tee /etc/systemd/system/lemp.service > /dev/null <<'EOF'
[Unit]
Description=LEMP stack managed by Docker Compose
Requires=docker.service
After=docker.service
[Service]
WorkingDirectory=/home/$(whoami)/lemp
ExecStart=/usr/local/lib/docker/cli-plugins/docker-compose up -d
ExecStop=/usr/local/lib/docker/cli-plugins/docker-compose down
TimeoutStartSec=0
Restart=always
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd and enable the service
sudo systemctl daemon-reload
sudo systemctl enable lemp.service
sudo systemctl start lemp.service
6. Secure the Deployment with a Firewall
Only HTTP (80) and SSH (22) should be reachable from the internet. Use UFW to enforce this.
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
7. Deploy on a Production‑Ready Cloud VPS
Running Docker Compose on a reliable virtual machine eliminates the overhead of managing multiple physical servers. You can rely on DevNix Cloud VPS to provide a clean Ubuntu environment, fast SSD storage, and a predictable network footprint—perfect for hosting containerized stacks.
8. Routine Maintenance Tips
- Update images:
docker compose pull && docker compose up -d - Backup volumes:
docker run --rm -v lemp_mariadb_data:/data -v $(pwd):/backup alpine tar czf /backup/mariadb.tar.gz -C /data . - Log rotation: Configure Docker’s
log-drivertojson-filewith amax-sizeof 10 MB to prevent disk exhaustion.
Conclusion
Docker Compose turns a multi‑service architecture into a single, version‑controlled file. By installing Docker Engine, adding the Compose plugin, defining a docker‑compose.yml, and wiring it into systemd, you gain reproducible deployments that survive reboots and scale with minimal effort. Pair this workflow with a robust Cloud VPS and you have a production‑grade environment ready for any web application.