Tillbaka till bloggen
Augusti 19, 2026Guider

Hur du driftsätter applikationer med Docker Compose på en VPS

En praktisk produktionsguide till Docker Compose v2 på Ubuntu: installera motorn, skriv en Compose-fil med nätverk och volymer, hantera hemligheter, sätt en omvänd proxy framför, uppdatera med lugna utrullningar och säkerhetskopiera namngivna volymer.

Hur du driftsätter applikationer med Docker Compose på en VPS

Docker Compose är det vanliga sättet att köra en liten stack på en VPS: en webbapp, en databas, en Redis-cache och en omvänd proxy, beskrivna i en YAML-fil. Det är inte Kubernetes. Du får inte failover över flera noder. Du får en upprepningsbar, granskningsbar uppsättning som du kan återskapa på en ny Hiddence-VPS på minuter — vilket är precis vad de flesta sidoprojekt och små produkter behöver.

Den här artikeln utgår från att du vill ha något närmare produktion än docker run en fredagskväll. Vi installerar Docker Engine och Compose-pluginet, skapar en deploy-användare utan root, skriver en Compose-fil med healthchecks och omstartspolicyer, håller hemligheter borta från Git, publicerar bara de omvända proxy-portarna mot internet och definierar en uppdaterings- och säkerhetskopieringsrutin. Exempelstacken är en typisk webbapp plus PostgreSQL plus Caddy, men samma mönster fungerar för Node, PHP, Python eller en samling workers.

Varför Compose på en enda VPS fortfarande är vettigt

Folk hoppar till Kubernetes för att det är på modet och lägger sedan helgen på YAML för en webbplats. Compose förblir läsbart. Du versionshanterar filen, dokumenterar miljövariabler och kan diff:a en ändring innan du tillämpar den. Isoleringen räcker: en komprometterad appcontainer ska inte hålla databasens UNIX-socket om du använt nätverk och användare med minsta behörighet. Resursgränser stoppar en minnesläcka från att frysa hela VPS:en.

  • En fil beskriver hela stacken
  • Namngivna volymer överlever att containrar återskapas
  • Interna Docker-nätverk håller Postgres borta från det publika internet
  • restart: unless-stopped täcker de flesta omstarter
  • Lätt att kopiera till en andra VPS när du växer ur den första

Krav

Använd en VPS med tillräckligt RAM för appen plus Postgres. En 2 GB-plan är ett realistiskt minimum för app + databas + proxy. Ubuntu 24.04 antas. Installera inte Docker från en slumpmässig Snap utan att läsa vad den gör med cgroups; stegen nedan använder Dockers officiella apt-arkiv.

  • Ubuntu 22.04/24.04, root eller sudo
  • 2 GB RAM rekommenderas (1 GB bara för små stackar utan Postgres)
  • En domän som pekar på VPS:en om du vill ha automatisk HTTPS
  • Git eller ett annat sätt att kopiera projektet till servern

Steg 1: Installera Docker Engine och Compose v2

Compose v2 är ett plugin som anropas som docker compose (med mellanslag), inte den gamla docker-compose Python-binären. Installera båda från Dockers arkiv så att du får aktuella säkerhetspatchar.

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

Steg 2: Deploy-användare och kataloglayout

Att köra Compose som root fungerar, men en dedikerad användare med medlemskap i docker-gruppen är renare. Lägg projektet i /opt eller /srv, inte i /root, så att säkerhetskopior och behörigheter är uppenbara. Gör aldrig katalogen som innehåller .env skrivbar för alla.

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

Steg 3: Skriv en produktionsinriktad Compose-fil

Filen nedan är en mall. Byt app-imagen mot din. Postgres publiceras inte till 0.0.0.0:5432 — bara Caddy publiceras på 80 och 443. Healthchecks stoppar en omvänd proxy från att skicka trafik till en app som fortfarande migrerar databasen. Lås image-taggar; latest är hur överraskningsbrott uppstår.

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:

Steg 4: Hemligheter, .env och Caddyfile

Lägg lösenord i .env på servern, chmod 600, och commita aldrig den filen. Generera lösenord med openssl rand -base64 32. Caddyfile behöver bara reverse-proxy:a till appens tjänstnamn på Docker-nätverket.

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

Steg 5: Brandvägg, loggar och resursgränser

UFW ska bara tillåta 22, 80 och 443. Docker kringgår ibland UFW för publicerade portar; om du behöver en strikt värdbrandvägg, slå upp den aktuella Docker + UFW-interaktionen för din Ubuntu-version eller publicera portar bara på 127.0.0.1 och sätt en värd-Caddy framför. Sätt minnesgränser i Compose så att Postgres inte kan äta upp VPS:en.

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

Steg 6: Uppdateringar och återställningar

Den tråkiga uppdateringen som fungerar: hämta nya images, up -d, bevaka healthchecks, behåll föregående tagg i git så att du kan låsa tillbaka. Kör inte docker compose down på en produktionsdatabas om du inte avser att sluta ta emot trafik. down -v tar bort volymer — det är en radering, inte en omstart.

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

Säkerhetskopiera namngivna volymer

En VPS-ögonblicksbild är bra. En logisk Postgres-dump är bättre om du behöver återställa på en annan maskin. Schemalägg detta med cron. Testa en återställning en gång, annars har du inte säkerhetskopior — du har filer du hoppas är säkerhetskopior.

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

Felsökning

Läs docker compose ps och docker compose logs service. Image-hämtningsfel är vanligtvis ghcr/docker hub-hastighetsgränser eller en privat image utan inloggning. 502 från Caddy betyder att appen inte är healthy eller att proxy-värdnamnet är fel (använd Compose-tjänstnamnet, inte localhost, inifrån Caddy-containern). Behörighetsfel på volymer betyder ofta att imagen körs som uid 1000 medan värdkatalogen är root.

  • compose: command not found — du installerade det gamla binärnamnet; använd docker compose
  • port is already allocated — något annat äger 80/443 (Apache, Nginx, en annan Caddy)
  • database connection refused — appen startade innan Postgres var redo; använd healthcheck + depends_on condition
  • disk full — docker system df, rensa sedan oanvända images försiktigt
  • permission denied on docker.sock — användaren är inte i docker-gruppen, eller så behöver du en ny inloggningssession

Säkerhet

Publicera inte databaserportar. Kör inte privileged: true. Montera inte /var/run/docker.sock i en appcontainer om du inte medvetet skriver en containerhanterare. Håll motorn uppdaterad. Skanna dina egna images om du bygger dem. .env på disk är fortfarande en hemlig fil — begränsa vem som kan ssh:a till maskinen.

  • Ingen publik 5432 / 3306 / 6379
  • Lås image-digests eller åtminstone oföränderliga taggar
  • chmod 600 .env
  • Unattended-upgrades för värd-OS:et spelar fortfarande roll
  • Ett Compose-projekt per app håller blast radius mindre

Tips

  • Lägg compose.yaml i git; behåll .env.example utan riktiga hemligheter
  • Använd profiler för valfria workers (docker compose --profile workers up -d)
  • Bevaka docker stats när du dimensionerar VPS:en
  • För bind mounts av källkod är det här inte produktionsmönstret — baka en image
  • Om stacken växer till flera VPS, titta då på orkestrering — inte innan

En produktionslik Compose-uppsättning på en VPS är: officiella Docker-paket, en nedlåst .env, en YAML-fil som inte publicerar databasen, healthchecks, en omvänd proxy på 80/443, UFW, image-taggar du kan rulla tillbaka och Postgres-dumpar du faktiskt har återställt en gång. Börja från mallen i den här artikeln, byt app-imagen och behandla docker compose down -v som ett destruktivt kommando med samma respekt som rm -rf.