Podstawy Docker #4: Docker Compose w praktyce

Docker Compose pozwala uruchamiać wiele kontenerów jednocześnie za pomocą jednego pliku konfiguracyjnego. Zamiast wpisywać długie komendy docker run, definiujesz wszystko w pliku docker-compose.yml. W praktyce to narzędzie, którego będziesz używać najczęściej.
Kiedy Compose, kiedy sam Docker?
Sam docker run wystarczy gdy:
- Uruchamiasz jeden kontener (np. szybki test)
- Robisz coś jednorazowo
Docker Compose gdy:
- Masz więcej niż jeden kontener (np. aplikacja + baza danych)
- Chcesz mieć powtarzalną konfigurację zapisaną w pliku
- Potrzebujesz wolumenów, sieci, zmiennych środowiskowych
- Pracujesz na serwerze produkcyjnym
💡 Praktyka
W praktyce prawie zawsze używasz Docker Compose — nawet dla jednego kontenera. Dlaczego? Bo masz konfigurację zapisaną w pliku, a nie w historii terminala. Łatwiej wrócić, zmodyfikować, przenieść na inny serwer.
Struktura pliku docker-compose.yml
⚠️ YAML wymaga wcięć spacjami
Pliki .yml wymagają wcięć spacjami (nie tabulatorami). Użyj 2 spacji na każdy poziom zagnieżdżenia. Jeden zły tabulator i plik nie zadziała.
Minimalny przykład:
services:
web:
image: caddy:2
ports:
- "8080:80"
To jest odpowiednik komendy:
docker run -d -p 8080:80 --name web caddy:2
Pełniejszy przykład
services:
app:
image: n8nio/n8n:latest
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=tajnehaslo
volumes:
- n8n_data:/home/node/.n8n
restart: unless-stopped
volumes:
n8n_data:
Kluczowe sekcje
| Sekcja | Co robi |
|---|---|
services |
Definicje kontenerów (każdy serwis = jeden kontener) |
image |
Jaki obraz użyć |
ports |
Mapowanie portów (host:kontener) |
environment |
Zmienne środowiskowe |
volumes |
Dane trwałe |
restart |
Polityka restartu |
networks |
Sieci |
Polityki restartu
| Wartość | Znaczenie |
|---|---|
no |
Nie restartuj (domyślne) |
always |
Restartuj zawsze (nawet po ręcznym stopie) |
unless-stopped |
Restartuj chyba że ręcznie zatrzymany |
on-failure |
Restartuj tylko po błędzie |
💡 Rekomendacja
Na serwerze produkcyjnym używaj restart: unless-stopped — kontener automatycznie wstanie po restarcie serwera, ale nie będzie się restartował jeśli go ręcznie zatrzymasz.
Komendy Docker Compose
Wszystkie komendy uruchamiaj z katalogu, w którym jest plik docker-compose.yml:
docker compose up -d # uruchom wszystkie serwisy w tle
docker compose down # zatrzymaj i usuń kontenery
docker compose ps # lista serwisów i ich status
docker compose logs # logi wszystkich serwisów
docker compose logs -f app # śledź logi konkretnego serwisu
docker compose restart # restart wszystkich serwisów
docker compose restart app # restart jednego serwisu
docker compose pull # pobierz najnowsze obrazy
docker compose up -d --pull always # pobierz obrazy i uruchom
Workflow aktualizacji
docker compose pull # pobierz nowe wersje obrazów
docker compose up -d # uruchom ponownie (podmieni kontenery)
💡 Aktualizacja bez downtime
docker compose up -d po pull automatycznie podmieni tylko te kontenery, których obraz się zmienił. Reszta zostaje nietknięta.
Przykład: Caddy z własną stroną
Utwórz katalog projektu:
mkdir -p ~/caddy-test && cd ~/caddy-test
Utwórz plik docker-compose.yml:
services:
web:
image: caddy:2
ports:
- "8080:80"
volumes:
- ./html:/usr/share/caddy:ro
restart: unless-stopped
Utwórz prostą stronę:
mkdir html
echo "<h1>Działa!</h1>" > html/index.html
Uruchom:
docker compose up -d
Wejdź na http://IP-SERWERA:8080 — zobaczysz „Działa!”.
Przykład: n8n + PostgreSQL + Redis + Caddy
Bardziej realistyczny przykład — n8n z bazą danych, cache i reverse proxy:
services:
n8n:
image: n8nio/n8n:latest
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=tajnehaslo
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
volumes:
- n8n_data:/home/node/.n8n
restart: unless-stopped
depends_on:
- db
- redis
db:
image: postgres:16
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=tajnehaslo
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
restart: unless-stopped
volumes:
n8n_data:
postgres_data:
caddy_data:
Zwróć uwagę na kilka ważnych rzeczy:
depends_on: db, redis— n8n startuje po bazie i Redis (uwaga: czeka aż kontener się uruchomi, ale NIE czeka aż baza będzie gotowa do połączeń)DB_POSTGRESDB_HOST: db— n8n łączy się z PostgreSQL po nazwie serwisu (db), nie po IP- Tylko Caddy ma
ports— reszta serwisów jest dostępna tylko wewnętrznie (bezpieczniejsze) - Każdy serwis ma wolumen — dane przetrwają restart kontenerów
💡 Scenariusz: n8n nie startuje
docker compose ps # czy wszystkie kontenery działają?
docker compose logs n8n # logi n8n
docker compose logs db # logi PostgreSQL — może hasło nie pasuje?
docker compose logs caddy # logi Caddy — może błąd w Caddyfile?
Plik .env — zmienne środowiskowe
Zamiast trzymać hasła w docker-compose.yml, utwórz plik .env w tym samym katalogu:
MYSQL_ROOT_PASSWORD=super_tajne_haslo
MYSQL_USER=wp_user
MYSQL_PASSWORD=inne_tajne_haslo
I w docker-compose.yml użyj zmiennych:
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
Zapis ${NAZWA} to zmienna — Docker podmieni ją na wartość z pliku .env. Dzięki temu hasła trzymasz w jednym pliku zamiast rozrzucać po konfiguracji.
⚠️ Bezpieczeństwo
Plik .env z hasłami nie powinien trafić do gita. Dodaj go do .gitignore.
Podsumowanie
| Komenda | Co robi |
|---|---|
docker compose up -d |
Uruchom serwisy w tle |
docker compose down |
Zatrzymaj i usuń kontenery |
docker compose ps |
Status serwisów |
docker compose logs -f |
Śledź logi |
docker compose pull |
Pobierz nowe obrazy |
docker compose restart |
Restart serwisów |
📚 Podstawy Docker — przewodnik
← Podstawy Docker #3: Komendy | Następny → Podstawy Docker #5: Wolumeny i sieci






