Docker Compose w produkcji — best practices
Opublikowano: 9 kwietnia 2026 · Kategoria: VPS / Docker
Docker Compose świetnie sprawdza się w developmencie. Ale produkcja ma inne wymagania: trwałe dane, izolacja sieci, bezpieczne sekrety, automatyczny restart po awarii. Oto kompletny przewodnik jak skonfigurować Compose na VPS tak, żeby nie budzić się w nocy z alertem.
Pełny docker-compose.yml dla produkcji
Przykładowy stack: aplikacja Node.js + PostgreSQL + Nginx reverse proxy:
services:
# Baza danych
db:
image: postgres:16-alpine
restart: unless-stopped
env_file: .env # Ładuje zmienne z pliku .env
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data # Named volume — persystentne dane
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Aplikacja
app:
image: myapp:latest
restart: unless-stopped
env_file: .env
environment:
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
NODE_ENV: production
networks:
- backend
- frontend
depends_on:
db:
condition: service_healthy # Czeka aż DB jest gotowa
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 512m # Limit RAM
cpus: '0.5' # Limit CPU
# Reverse proxy
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Bind mount — config
- ./nginx/certs:/etc/nginx/certs:ro # Certyfikaty SSL
- nginx_cache:/var/cache/nginx # Named volume — cache
networks:
- frontend
depends_on:
- app
volumes:
db_data:
driver: local
nginx_cache:
driver: local
networks:
backend:
driver: bridge
internal: true # Brak dostępu z zewnątrz — tylko między serwisami
frontend:
driver: bridge Plik .env — bezpieczne sekrety
Plik .env trzyma wrażliwe dane. Nigdy nie commituj go do gita:
# .env (dodaj do .gitignore!) DB_NAME=myapp_prod DB_USER=myapp DB_PASSWORD=super_secret_password_here SECRET_KEY=32_char_random_string_here REDIS_URL=redis://redis:6379 # Uprawnienia — tylko właściciel może czytać chmod 600 .env
# .env.example (commituj — szablon bez wartości) DB_NAME= DB_USER= DB_PASSWORD= SECRET_KEY= REDIS_URL=
Healthchecks — automatyczna detekcja awarii
Healthchecks pozwalają Compose (i Docker) wiedzieć czy kontener naprawdę działa, a nie tylko
że proces się uruchomił. Stany kontenera: starting → healthy lub unhealthy. Przy unhealthy można automatycznie zrestartować.
| Parametr | Opis | Wartość domyślna |
|---|---|---|
interval | Co ile sprawdzać | 30s |
timeout | Czas oczekiwania na odpowiedź | 30s |
retries | Ile nieudanych prób = unhealthy | 3 |
start_period | Grace period przy starcie (błędy nie liczą się) | 0s |
Deploy i aktualizacje
# Podstawowe komendy produkcyjne # Uruchom w tle docker compose up -d # Sprawdź stan docker compose ps docker compose logs -f app # Aktualizacja aplikacji (pobierz nowy obraz + restart) docker compose pull app docker compose up -d app # Zatrzymuje i startuje tylko zmieniony serwis # Pełna aktualizacja wszystkich serwisów docker compose pull docker compose up -d # Zatrzymaj bez usuwania danych docker compose stop # Usuń kontenery (dane w volumes są bezpieczne) docker compose down # NIEBEZPIECZNE: usuń kontenery + volumes (utrata danych!) docker compose down -v
Backup named volumes
# Backup named volume do pliku tar
docker run --rm \
-v myapp_db_data:/data \
-v ${PWD}/backups:/backup \
alpine tar czf /backup/db_data_$(date +%Y%m%d).tar.gz -C /data .
# Restore z backupu
docker run --rm \
-v myapp_db_data:/data \
-v ${PWD}/backups:/backup \
alpine tar xzf /backup/db_data_20260409.tar.gz -C /data Traefik zamiast Nginx — automatyczne SSL
Traefik to nowoczesna alternatywa dla Nginx jako reverse proxy w środowisku Docker. Automatycznie wykrywa kontenery i wystawia certyfikaty Let's Encrypt bez konfiguracji ręcznej:
services:
traefik:
image: traefik:v3
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "[email protected]"
- "--certificatesresolvers.letsencrypt.acme.storage=/certs/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik_certs:/certs
networks:
- frontend
app:
image: myapp:latest
restart: unless-stopped
networks:
- frontend
- backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`example.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=3000"
volumes:
traefik_certs:
networks:
frontend:
backend:
internal: true Best practices — lista kontrolna
- restart: unless-stopped na wszystkich serwisach produkcyjnych — kontenery restartują po restarcie hosta.
- Limity zasobów — bez
deploy.resources.limitsjeden serwis może zagłodzić pozostałe z pamięci. - Internal networks — baza danych nie powinna mieć dostępu do internetu. Użyj
internal: true. - Read-only bind mounts — dodaj
:rodla plików konfiguracyjnych (Nginx, certyfikaty). - Pinuj wersje obrazów —
postgres:16-alpinezamiastpostgres:latest. Unikasz niespodzianek po auto-update. - Logowanie — ustaw
logging.driver: json-filezmax-sizeimax-fileżeby logi nie zapełniły dysku.