Tornar al blog
Agost 19, 2026Guies

Com desplegar aplicacions amb Docker Compose en un VPS

Una guia pràctica de producció de Docker Compose v2 a l'Ubuntu: instal·leu el motor, escriviu un fitxer Compose amb xarxes i volums, gestioneu secrets, poseu un servidor intermediari invers al davant, actualitzeu amb desplegaments sense pànic i feu còpies de volums amb nom.

Com desplegar aplicacions amb Docker Compose en un VPS

Docker Compose és la manera habitual d'executar una pila petita en un sol VPS: una aplicació web, una base de dades, una memòria cau Redis i un servidor intermediari invers, descrits en un sol fitxer YAML. No és Kubernetes. No obtindreu commutació per error multinode. Obtindreu una configuració repetible i revisable que podeu recrear en un VPS Hiddence nou en minuts — exactament el que necessiten la majoria de projectes secundaris i productes petits.

Aquest article assumeix que voleu alguna cosa més a prop de producció que un docker run un divendres a la nit. Instal·larem Docker Engine i el connector Compose, crearem un usuari de desplegament no root, escriurem un fitxer Compose amb comprovacions de salut i polítiques de reinici, mantindrem els secrets fora de Git, publicarem només els ports del servidor intermediari invers a internet i definirem una rutina d'actualització i còpia de seguretat. La pila d'exemple és una aplicació web típica més PostgreSQL més Caddy, però el mateix patró funciona per a Node, PHP, Python o un munt de workers.

Per què Compose en un sol VPS encara té sentit

La gent salta a Kubernetes perquè està de moda i després es passa el cap de setmana amb YAML per a un sol lloc web. Compose es manté llegible. Feu versions del fitxer, documenteu les variables d'entorn i podeu fer un diff d'un canvi abans d'aplicar-lo. L'aïllament n'hi ha prou: un contenidor d'aplicació compromès no hauria de tenir el sòcol UNIX de la base de dades si heu fet servir xarxes i usuaris de mínim privilegi. Els límits de recursos impedeixen que una fuita de memòria congeli tot el VPS.

  • Un sol fitxer descriu tota la pila
  • Els volums amb nom sobreviuen a la recreació de contenidors
  • Les xarxes internes de Docker mantenen Postgres fora d'internet pública
  • restart: unless-stopped cobreix la majoria de reinicis
  • Fàcil de copiar a un segon VPS quan us quedeu petit el primer

Requisits

Utilitzeu un VPS amb prou RAM per a l'aplicació més Postgres. Un pla de 2 GB és un mínim realista per a aplicació + base de dades + proxy. S'assumeix Ubuntu 24.04. No instal·leu Docker des d'un Snap aleatori sense llegir què fa als cgroups; els passos següents fan servir el repositori apt oficial de Docker.

  • Ubuntu 22.04/24.04, root o sudo
  • Es recomanen 2 GB de RAM (1 GB només per a piles minúscules sense Postgres)
  • Un domini que apunti al VPS si voleu HTTPS automàtic
  • Git o una altra manera de copiar el projecte al servidor

Pas 1: Instal·leu Docker Engine i Compose v2

Compose v2 és un connector que s'invoca com a docker compose (amb un espai), no el binari Python antic docker-compose. Instal·leu tots dos des del repositori de Docker per obtenir pedaços de seguretat actuals.

bash
ssh root@YOUR_VPS_IP
apt update && apt -y install ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

docker version
docker compose version

Pas 2: Usuari de desplegament i disposició de directoris

Executar Compose com a root funciona, però un usuari dedicat amb pertinença al grup docker és més net. Poseu el projecte a /opt o /srv, no a /root, perquè les còpies de seguretat i els permisos siguin òbvies. Mai no feu escriptura mundial el directori que conté .env.

bash
adduser --disabled-password --gecos '' deploy
usermod -aG docker deploy
mkdir -p /srv/app
chown deploy:deploy /srv/app
chmod 750 /srv/app

# Log in as deploy for the rest of the file editing:
# su - deploy
# cd /srv/app

Pas 3: Escriviu un fitxer Compose pensat per a producció

El fitxer següent és una plantilla. Substituïu la imatge de l'aplicació per la vostra. Postgres no es publica a 0.0.0.0:5432 — només Caddy es publica als 80 i 443. Les comprovacions de salut impedeixen que un servidor intermediari invers enviï trànsit a una aplicació que encara està migrant la base de dades. Fixeu les etiquetes d'imatge; latest és com arriben les trencadisses per sorpresa.

bash
# /srv/app/compose.yaml
services:
  caddy:
    image: caddy:2.8-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      app:
        condition: service_healthy

  app:
    image: ghcr.io/example/webapp:1.4.2
    restart: unless-stopped
    env_file: .env
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/health"]
      interval: 10s
      timeout: 3s
      retries: 10
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: .env
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - backend

networks:
  frontend: {}
  backend: {}

volumes:
  pgdata:
  caddy_data:
  caddy_config:

Pas 4: Secrets, .env i Caddyfile

Poseu les contrasenyes a .env al servidor, chmod 600, i no confirmeu mai aquest fitxer. Genereu contrasenyes amb openssl rand -base64 32. El Caddyfile només ha de fer reverse-proxy al nom del servei de l'aplicació a la xarxa Docker.

bash
# /srv/app/.env (permissions 600)
POSTGRES_USER=app
POSTGRES_PASSWORD=change-me-long-random
POSTGRES_DB=app
# plus any APP_SECRET / NEXTAUTH_SECRET your image needs

# /srv/app/Caddyfile
app.example.com {
    encode gzip
    reverse_proxy app:8000
}

chmod 600 /srv/app/.env
cd /srv/app
docker compose up -d
docker compose ps
docker compose logs -f --tail=100

Pas 5: Tallafoc, registres i límits de recursos

UFW hauria de permetre només 22, 80 i 443. Docker de vegades evita UFW per als ports publicats; si necessiteu un tallafoc d'amfitrió estricte, consulteu la interacció actual Docker + UFW per a la vostra versió d'Ubuntu o publiqueu ports només a 127.0.0.1 i poseu un Caddy d'amfitrió al davant. Establiu límits de memòria a Compose perquè Postgres no es pugui menjar el VPS.

bash
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Optional in compose.yaml under app or db:
#    deploy:
#      resources:
#        limits:
#          memory: 512M

# Logs (do not let json-file grow forever):
# { "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }
# in /etc/docker/daemon.json then: systemctl restart docker

Pas 6: Actualitzacions i retrocés

L'actualització avorrida que funciona: baixeu imatges noves, up -d, vigileu les comprovacions de salut, conserveu l'etiqueta anterior a git per poder tornar-la a fixar. No feu docker compose down en una base de dades de producció tret que pretengueu deixar d'acceptar trànsit. down -v elimina volums — això és un esborrat, no un reinici.

bash
cd /srv/app
git pull   # if the Compose file lives in git

# Change the image tag in compose.yaml, then:
docker compose pull
docker compose up -d
docker compose ps

# Rollback: set the old tag, pull, up -d again
# NEVER: docker compose down -v   # this deletes named volumes

Feu còpia dels volums amb nom

Una instantània del VPS està bé. Un bolcat lògic de Postgres és millor si heu de restaurar en una altra màquina. Programeu-ho amb cron. Proveu una restauració una vegada, o no teniu còpies de seguretat — teniu fitxers que espereu que ho siguin.

bash
# Postgres dump while the db container is running:
docker compose exec -T db pg_dump -U app app | gzip > /var/backups/app-$(date +%F).sql.gz

# Copy off-box
# scp /var/backups/app-*.sql.gz backup-host:~

# Restore sketch (maintenance window):
# gunzip -c app-2026-08-19.sql.gz | docker compose exec -T db psql -U app app

Resolució de problemes

Llegiu docker compose ps i docker compose logs service. Els errors de baixada d'imatge solen ser límits de velocitat de ghcr/docker hub o una imatge privada sense inici de sessió. Un 502 de Caddy vol dir que l'aplicació no està sana o que el nom d'amfitrió del proxy és incorrecte (utilitzeu el nom del servei Compose, no localhost, des de dins del contenidor Caddy). Els errors de permís als volums sovint volen dir que la imatge s'executa com a uid 1000 mentre el directori de l'amfitrió és root.

  • compose: command not found — heu instal·lat el nom de binari antic; utilitzeu docker compose
  • el port ja està assignat — alguna altra cosa té el 80/443 (Apache, Nginx, un altre Caddy)
  • connexió a la base de dades refusada — l'aplicació ha arrencat abans que Postgres estigués llest; utilitzeu healthcheck + condició depends_on
  • disc ple — docker system df, després feu prune d'imatges no utilitzades amb compte
  • permís denegat a docker.sock — l'usuari no és al grup docker, o necessiteu una sessió d'inici nova

Seguretat

No publiqueu ports de base de dades. No executeu privileged: true. No munteu /var/run/docker.sock en un contenidor d'aplicació tret que estigueu escrivint un gestor de contenidors a propòsit. Mantingueu el motor actualitzat. Escanegeu les vostres pròpies imatges si les construïu. .env al disc encara és un fitxer secret — restringiu qui pot fer ssh a la màquina.

  • Sense 5432 / 3306 / 6379 públics
  • Fixeu digests d'imatge o almenys etiquetes immutables
  • chmod 600 .env
  • Unattended-upgrades per al sistema operatiu de l'amfitrió encara importen
  • Un projecte Compose per aplicació manté el radi d'explosió més petit

Consells

  • Poseu compose.yaml a git; conserveu .env.example sense secrets reals
  • Utilitzeu perfils per a workers opcionals (docker compose --profile workers up -d)
  • Vigileu docker stats quan dimensioneu el VPS
  • Per a muntatges bind del codi font, aquest no és el patró de producció — coueu una imatge
  • Si la pila creix a diversos VPS, llavors mireu l'orquestració — no abans

Una configuració Compose gairebé de producció en un sol VPS és: paquets oficials de Docker, un .env protegit, un fitxer YAML que no publica la base de dades, comprovacions de salut, un servidor intermediari invers als 80/443, UFW, etiquetes d'imatge que podeu revertir i bolcats de Postgres que realment heu restaurat una vegada. Comenceu per la plantilla d'aquest article, substituïu la imatge de l'aplicació i tracteu docker compose down -v com una ordre destructiva amb el mateix respecte que rm -rf.