Docker Compose w produkcji: 10 błędów, które mogą zabić Twój serwer

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ć.

 

Bezpłatne warsztaty: Zbuduj 5 Agentów AI
Sprawdź szczegóły:

Spis treści

  1. Dlaczego Docker Compose na produkcji bywa niebezpieczny
  2. Błąd 1: Brak limitów zasobów (CPU i RAM)
  3. Błąd 2: Używanie tagu latest zamiast wersji
  4. Błąd 3: Brak polityki restartu (restart policy)
  5. Błąd 4: Sekrety i hasła w plain text
  6. Błąd 5: Brak health checków
  7. Błąd 6: Złe zarządzanie wolumenami i utrata danych
  8. Błąd 7: Brak rotacji logów – dysk zapełniony w 100%
  9. Błąd 8: Uruchamianie kontenerów jako root
  10. Błąd 9: Wszystkie kontenery w jednej sieci bez izolacji
  11. Błąd 10: Brak strategii backupu i disaster recovery
  12. Checklist: bezpieczny docker-compose.yml na produkcję
  13. 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.resources działa natywnie tylko z docker stack deploy (Swarm). Przy zwykłym docker compose up w nowszych wersjach Docker Compose (v2.x) limity deploy.resources.limits są respektowane, ale warto to zweryfikować wersją docker compose version i w razie potrzeby użyć starszej flagi mem_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
nobrak automatycznego restartu (domyślne, niebezpieczne na produkcji)
alwaysrestart zawsze, nawet po ręcznym docker stop
unless-stoppedrestart zawsze, poza przypadkiem ręcznego zatrzymania (zalecane)
on-failurerestart 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 -v na 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 cpus i memory dla każdego serwisu
  • [ ] Wszystkie obrazy przypięte do konkretnej wersji (nie latest)
  • [ ] restart: unless-stopped (lub always) dla usług krytycznych
  • [ ] Sekrety poza repozytorium, w .env z .gitignore lub w secrets
  • [ ] healthcheck zdefiniowany 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.sock niezamontowany 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.

 



Bezpłatne warsztaty: Zbuduj 5 Agentów AI

Sprawdź szczegóły:

https://asdevops.pl/warsztaty/

 

Bezpłatne warsztaty - Zbuduj 5 Agentów AI

X