Docker Compose to jedno z najczęściej używanych narzędzi do uruchamiania aplikacji kontenerowych – nie tylko lokalnie, ale też na produkcyjnych serwerach VPS, dedykowanych maszynach czy małych klastrach. Problem w tym, że konfiguracja, która świetnie działa na laptopie developera, na produkcji potrafi doprowadzić do awarii całego serwera, wycieku danych albo utraty wszystkich wolumenów w ciągu kilku sekund.
W tym artykule znajdziesz 10 najczęstszych i najbardziej kosztownych błędów popełnianych przy wdrażaniu Docker Compose na produkcji – wraz z konkretnymi przykładami konfiguracji, wyjaśnieniem dlaczego dany błąd jest niebezpieczny oraz gotowym rozwiązaniem, które możesz od razu zastosować.
Spis treści
- Dlaczego Docker Compose na produkcji bywa niebezpieczny
- Błąd 1: Brak limitów zasobów (CPU i RAM)
- Błąd 2: Używanie tagu
latestzamiast wersji - Błąd 3: Brak polityki restartu (
restart policy) - Błąd 4: Sekrety i hasła w plain text
- Błąd 5: Brak health checków
- Błąd 6: Złe zarządzanie wolumenami i utrata danych
- Błąd 7: Brak rotacji logów – dysk zapełniony w 100%
- Błąd 8: Uruchamianie kontenerów jako root
- Błąd 9: Wszystkie kontenery w jednej sieci bez izolacji
- Błąd 10: Brak strategii backupu i disaster recovery
- Checklist: bezpieczny
docker-compose.ymlna produkcję - FAQ – najczęstsze pytania o Docker Compose w produkcji
1. Dlaczego Docker Compose na produkcji bywa niebezpieczny
Docker Compose został zaprojektowany przede wszystkim jako narzędzie deweloperskie – do szybkiego uruchamiania środowisk lokalnych. Nie ma wbudowanego mechanizmu orkiestracji na poziomie klastra (jak Kubernetes), nie ma automatycznego failover, nie ma natywnego zarządzania sekretami na poziomie enterprise.
To nie znaczy, że nie da się go bezpiecznie używać na produkcji – tysiące małych i średnich firm robi to codziennie. Problem pojawia się wtedy, gdy zespół kopiuje plik docker-compose.yml napisany „na szybko” do środowiska deweloperskiego wprost na serwer produkcyjny, bez żadnych modyfikacji. Poniższe 10 błędów to najczęstsze przyczyny padów serwerów, wycieków danych i nieodwracalnej utraty baz danych.
2. Błąd 1: Brak limitów zasobów (CPU i RAM)
To najczęstsza przyczyna całkowitego zawieszenia serwera. Bez zdefiniowanych limitów jeden kontener – np. po wycieku pamięci w aplikacji Node.js albo po ataku DDoS na endpoint API – może pochłonąć całą dostępną pamięć RAM i CPU maszyny. System operacyjny w takiej sytuacji zaczyna „zabijać” losowe procesy (OOM Killer), co często prowadzi do awarii nie tylko aplikacji, ale też samego SSH czy monitoringu.
Błędna konfiguracja
services:
api:
image: my-api:1.0
ports:
- "3000:3000"
Poprawna konfiguracja
services:
api:
image: my-api:1.0
ports:
- "3000:3000"
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
Uwaga: sekcja
deploy.resourcesdziała natywnie tylko zdocker stack deploy(Swarm). Przy zwykłymdocker compose upw nowszych wersjach Docker Compose (v2.x) limitydeploy.resources.limitssą respektowane, ale warto to zweryfikować wersjądocker compose versioni w razie potrzeby użyć starszej flagimem_limit/cpus.
3. Błąd 2: Używanie tagu latest zamiast wersji
image: postgres:latest wygląda niewinnie, ale to jeden z najbardziej podstępnych błędów. Gdy zrobisz docker compose pull albo serwer zresetuje kontener po restarcie hosta, możesz nieświadomie pobrać nową, niekompatybilną wersję obrazu – np. Postgres 17 zamiast 15 – co skutkuje błędami migracji, niekompatybilnością formatu danych na wolumenie, a w najgorszym wypadku brakiem możliwości uruchomienia bazy w ogóle.
Błędna konfiguracja
services:
db:
image: postgres:latest
Poprawna konfiguracja
services:
db:
image: postgres:16.4
Zawsze przypinaj konkretną wersję, a najlepiej też digest obrazu (@sha256:...), jeśli zależy Ci na pełnej powtarzalności builda:
services:
db:
image: postgres@sha256:2f3b0a...c91
4. Błąd 3: Brak polityki restartu (restart policy)
Domyślna wartość restart w Docker Compose to no. Oznacza to, że jeśli kontener padnie – z powodu chwilowego braku pamięci, błędu aplikacji albo restartu serwera – nie uruchomi się ponownie automatycznie. Efekt: aplikacja jest niedostępna, dopóki ktoś ręcznie nie zaloguje się i nie wykona docker compose up -d.
Błędna konfiguracja
services:
web:
image: nginx:1.27
Poprawna konfiguracja
services:
web:
image: nginx:1.27
restart: unless-stopped
| Wartość | Zachowanie |
|---|---|
no | brak automatycznego restartu (domyślne, niebezpieczne na produkcji) |
always | restart zawsze, nawet po ręcznym docker stop |
unless-stopped | restart zawsze, poza przypadkiem ręcznego zatrzymania (zalecane) |
on-failure | restart tylko po błędzie (kod wyjścia ≠ 0) |
5. Błąd 4: Sekrety i hasła w plain text
Trzymanie haseł do baz danych, kluczy API czy tokenów JWT bezpośrednio w docker-compose.yml i wrzucenie tego pliku do publicznego (albo nawet prywatnego, ale współdzielonego) repozytorium Git to jeden z najczęstszych powodów włamań na serwery produkcyjne.
Błędna konfiguracja
services:
db:
image: postgres:16.4
environment:
POSTGRES_PASSWORD: SuperTajneHaslo123
Poprawna konfiguracja
Użyj pliku .env dodanego do .gitignore albo natywnego mechanizmu secrets:
services:
db:
image: postgres:16.4
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
Dodatkowo warto rozważyć zewnętrzny menedżer sekretów (HashiCorp Vault, Doppler, AWS Secrets Manager) — szczególnie gdy serwer obsługuje dane wrażliwe (RODO/GDPR).
6. Błąd 5: Brak health checków
Bez zdefiniowanego healthcheck, Docker uznaje kontener za „działający”, dopóki proces wewnątrz nie zakończy działania. Problem w tym, że aplikacja może żyć, ale nie odpowiadać – np. utknąć w deadlocku, stracić połączenie z bazą danych albo przestać obsługiwać requesty, mimo że proces PID 1 wciąż działa. Bez health checka load balancer czy reverse proxy nadal będzie kierował ruch do martwego kontenera.
Poprawna konfiguracja
services:
api:
image: my-api:1.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
W połączeniu z depends_on: condition: service_healthy można wymusić, aby np. backend startował dopiero po realnej gotowości bazy danych:
services:
api:
depends_on:
db:
condition: service_healthy
7. Błąd 6: Złe zarządzanie wolumenami i utrata danych
To błąd, który kończy się nieodwracalną utratą całej bazy danych. Najczęstsza przyczyna: dane bazy trzymane są w anonimowym wolumenie (albo, co gorsza, wewnątrz warstwy kontenera), a zespół podczas „czyszczenia” serwera wykonuje docker compose down -v lub docker system prune -a --volumes, bezpowrotnie kasując produkcyjne dane.
Błędna konfiguracja
services:
db:
image: postgres:16.4
# brak zdefiniowanego wolumenu — dane żyją w warstwie kontenera
Poprawna konfiguracja
services:
db:
image: postgres:16.4
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
driver: local
Dodatkowe zasady bezpieczeństwa:
- Nigdy nie używaj
docker compose down -vna produkcji bez wcześniejszego backupu. - Rozważ named volumes zamiast bind mountów w przypadku baz danych – są bardziej odporne na przypadkowe skasowanie z poziomu hosta.
- Regularnie testuj, czy backup faktycznie da się odtworzyć (patrz błąd 10: Brak strategii backupu i disaster recovery).
8. Błąd 7: Brak rotacji logów – dysk zapełniony w 100%
Docker domyślnie zapisuje logi kontenerów w formacie JSON bez limitu rozmiaru. Aplikacja, która intensywnie loguje (np. w pętli błędów albo przy dużym ruchu), potrafi w kilka dni zapełnić cały dysk serwera. Gdy dysk osiąga 100%, padają nie tylko kontenery – może przestać działać nawet system operacyjny, baza danych czy proces SSH.
Błędna konfiguracja
services:
api:
image: my-api:1.0
# brak konfiguracji logów
Poprawna konfiguracja
services:
api:
image: my-api:1.0
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Dla większych środowisk warto wysyłać logi od razu do zewnętrznego systemu (Loki, ELK, CloudWatch) zamiast trzymać je lokalnie na dysku produkcyjnym.
9. Błąd 8: Uruchamianie kontenerów jako root
Domyślnie procesy wewnątrz kontenera Docker działają jako root. Jeśli atakujący znajdzie podatność w aplikacji (np. RCE w bibliotece), a kontener działa jako root i ma dostęp do wrażliwych bind mountów albo (jeszcze gorzej) do gniazda Dockera (/var/run/docker.sock), może to prowadzić do przejęcia całego hosta, nie tylko kontenera.
Błędna konfiguracja
services:
api:
image: my-api:1.0
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Montowanie docker.sock do kontenera aplikacyjnego to jeden z najniebezpieczniejszych wzorców — praktycznie oddaje pełną kontrolę nad hostem każdemu, kto przejmie ten kontener.
Poprawna konfiguracja
services:
api:
image: my-api:1.0
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
Najlepiej też definiować USER bezpośrednio w Dockerfile, zamiast polegać wyłącznie na docker-compose.yml.
10. Błąd 9: Wszystkie kontenery w jednej sieci bez izolacji
Domyślnie Docker Compose tworzy jedną wspólną sieć dla wszystkich serwisów w pliku. Oznacza to, że np. kontener frontendowy, wystawiony publicznie, może bez żadnych ograniczeń komunikować się bezpośrednio z bazą danych czy panelem admina – mimo że nigdy nie powinien mieć do nich dostępu.
Błędna konfiguracja
services:
web:
image: my-frontend:1.0
db:
image: postgres:16.4
admin:
image: pgadmin4:8.0
Poprawna konfiguracja – segmentacja sieci
services:
web:
image: my-frontend:1.0
networks: [frontend]
api:
image: my-api:1.0
networks: [frontend, backend]
db:
image: postgres:16.4
networks: [backend]
admin:
image: pgadmin4:8.0
networks: [backend]
# brak wystawionych portów na zewnątrz!
networks:
frontend:
backend:
internal: true
Sieć internal: true uniemożliwia bezpośredni dostęp z internetu do usług backendowych – komunikacja odbywa się wyłącznie przez zdefiniowany serwis API.
11. Błąd 10: Brak strategii backupu i disaster recovery
Nawet przy idealnej konfiguracji docker-compose.yml serwer może paść z powodów niezależnych od Ciebie: awaria dysku, błąd dostawcy VPS, przypadkowe rm -rf, atak ransomware. Brak zautomatyzowanego, regularnie testowanego backupu to błąd, który potrafi zakończyć istnienie firmy, a nie tylko projektu.
Przykład automatycznego backupu bazy danych
services:
db-backup:
image: postgres:16.4
depends_on:
- db
volumes:
- ./backups:/backups
entrypoint: >
sh -c "while true; do
pg_dump -h db -U postgres mydb > /backups/backup_$$(date +%Y%m%d_%H%M%S).sql;
find /backups -type f -mtime +7 -delete;
sleep 86400;
done"
Zasada 3-2-1
- 3 kopie danych,
- na 2 różnych nośnikach/technologiach,
- 1 kopia przechowywana poza lokalizacją serwera (np. S3, inny region).
Backup, który nigdy nie był testowany przez odtworzenie, w praktyce nie istnieje – regularnie sprawdzaj, czy plik .sql faktycznie da się zaimportować do świeżej bazy.
12. Checklist: bezpieczny docker-compose.yml na produkcję
- [ ] Zdefiniowane limity
cpusimemorydla każdego serwisu - [ ] Wszystkie obrazy przypięte do konkretnej wersji (nie
latest) - [ ]
restart: unless-stopped(lubalways) dla usług krytycznych - [ ] Sekrety poza repozytorium, w
.envz.gitignorelub wsecrets - [ ]
healthcheckzdefiniowany dla kluczowych usług - [ ] Dane trwałe w nazwanych wolumenach, nigdy w warstwie kontenera
- [ ] Rotacja logów (
max-size,max-file) skonfigurowana - [ ] Kontenery uruchamiane jako non-root,
docker.sockniezamontowany do aplikacji - [ ] Sieci podzielone na strefy (
frontend/backend/internal) - [ ] Zautomatyzowany i regularnie testowany backup
13. FAQ — najczęstsze pytania o Docker Compose w produkcji
Czy Docker Compose nadaje się do produkcji?
Tak, dla małych i średnich projektów jest w pełni wystarczający, pod warunkiem wdrożenia limitów zasobów, health checków, backupów i segmentacji sieci opisanych w tym artykule. Przy większej skali (wiele node’ów, auto-scaling) lepszym wyborem bywa Kubernetes lub Docker Swarm.
Jaki jest najczęstszy powód padania serwera z Docker Compose?
Najczęściej brak limitów memory/cpus, co prowadzi do wyczerpania zasobów hosta przez jeden „zbuntowany” kontener i uruchomienia OOM Killera przez system operacyjny.
Czy restart: always i restart: unless-stopped to to samo?
Nie. always uruchomi kontener ponownie nawet po ręcznym docker stop, natomiast unless-stopped respektuje ręczne zatrzymanie — to zwykle bezpieczniejszy wybór na produkcji.
Jak zapobiec utracie danych po docker compose down -v?
Używaj nazwanych wolumenów, ogranicz dostęp do polecenia down -v tylko do zaufanych osób/skryptów CI oraz zawsze miej aktualny, przetestowany backup przed jakimikolwiek operacjami porządkującymi na produkcji.
Czy montowanie /var/run/docker.sock do kontenera jest bezpieczne?
Nie, jest to jedna z najbardziej ryzykownych praktyk — daje kontenerowi praktycznie pełną kontrolę nad hostem. Rób to wyłącznie w zaufanych, izolowanych narzędziach administracyjnych, nigdy w kontenerach aplikacyjnych wystawionych publicznie.
Artykuł ma charakter edukacyjny. Przed wdrożeniem zmian na produkcyjnym serwerze zawsze testuj konfigurację na środowisku staging i wykonaj pełny backup danych.

