SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor

Docker Compose na VPS: instalacja i konfiguracja

Instrukcja instalacji Docker Engine na Ubuntu 24.04. Dowiedz się, jak poprawnie skonfigurować plik compose, zarządzać wolumenami i uniknąć problemów z regułami ufw.

Co budujesz

Docker Compose stanowi fundament niemal wszystkich rozwiązań opisanych w tym serwisie. Nextcloud, Vaultwarden, n8n, Immich, Rocket.Chat – każdy z tych przewodników zaczyna się od polecenia utworzenia pliku compose, a niniejsza strona wyjaśnia, co ten plik faktycznie oznacza. Zainstalujesz Docker Engine oraz wtyczkę Compose z oficjalnego repozytorium apt firmy Docker na systemie Ubuntu 24.04, a następnie uruchomisz stos składający się z dwóch usług: czytnika RSS Miniflux oraz bazy danych PostgreSQL. Ta para pozwala przećwiczyć wszystkie wzorce wykorzystywane przez większe aplikacje: przypięte wersje obrazów, bazę danych z mechanizmem healthcheck, wolumen nazwany, sekrety w pliku .env oraz port udostępniony wyłącznie na localhost.

Instalacja zajmuje pięć minut. Pozostała część przewodnika omawia kwestie, które mogą sprawiać trudności w przyszłości: grupę docker będącą w praktyce kontem root, porty publikowane z pominięciem reguł ufw oraz flagę w docker compose down, która usuwa bazę danych bez prośby o potwierdzenie.

Wymagania wstępne: świeży serwer VPS KVM z systemem Ubuntu 24.04, użytkownik z uprawnieniami sudo oraz co najmniej jeden gigabajt pamięci RAM. Istniejąca instalacja Docker również jest dopuszczalna; pierwsza sekcja zawiera informacje o tym, co należy usunąć.

Instalacja z repozytorium Docker, a nie Ubuntu

Przed wykonaniem pierwszego polecenia należy uniknąć dwóch błędów. Pakiet docker.io z repozytorium Ubuntu działa, ale jest przestarzały względem wydań Docker i nie posiada struktury wtyczek, której wymagają inne komponenty. Polecenie docker-compose z łącznikiem to ścieżka, na którą kierują starsze poradniki. Na starszym serwerze polecenie to zazwyczaj odnosi się do Compose v1, napisanego w Pythonie, którego wsparcie zostało zakończone przez Docker; jest to powód, dla którego stare poradniki przestają działać. Docker nadal udostępnia samodzielny plik binarny docker-compose, zbudowany na bazie współczesnego kodu Go, jednak oficjalna dokumentacja odradza tę metodę i wspiera ją jedynie ze względu na kompatybilność wsteczną. Obecnie Compose to docker compose (z odstępem), wtyczka CLI instalowana z tego samego repozytorium co silnik.

Jeśli którykolwiek z tych składników znajduje się już w systemie, należy go usunąć, w tym docker-compose-v2 oraz docker-buildx (pakiety wtyczek dostarczane przez Ubuntu), aby zapewnić spójność pochodzenia oprogramowania:

sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc

Package 'docker.io' is not installed, so not removed to standardowy wynik na świeżym VPS. Następnie należy dodać repozytorium Docker i przeprowadzić instalację:

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo 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" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Należy zweryfikować wszystkie trzy warstwy:

docker --version
docker compose version
sudo docker run --rm hello-world

Dwa pierwsze polecenia wyświetlają wersje, a Docker Compose version v5.x.x potwierdza obecność wtyczki, a nie przestarzałego pliku binarnego v1. W grudniu 2025 roku wersja wtyczki przeskoczyła z v2 bezpośrednio na v5, pomijając 3 i 4, aby uniknąć niejasności z formatami plików compose. Poradniki wskazujące v2.x.x opisują to samo narzędzie. Uruchomienie hello-world powinno zakończyć się wynikiem Hello from Docker!. Pakiet automatycznie włącza usługę przy starcie systemu; systemctl is-enabled docker wyświetla enabled.

Grupa docker to root, podejmuj decyzję świadomie

Obecnie każde polecenie docker wymaga użycia sudo, ponieważ gniazdo demona w /var/run/docker.sock jest własnością użytkownika root oraz grupy docker. Bez przynależności do tej grupy otrzymasz najczęściej wyszukiwany błąd Docker:

permission denied while trying to connect to the Docker daemon socket at
unix:///var/run/docker.sock

Standardowe rozwiązanie:

sudo usermod -aG docker $USER

Przynależność do grupy jest weryfikowana przy logowaniu, więc błąd będzie nadal występował w bieżącej powłoce. Uruchom newgrp docker dla tej sesji lub wyloguj się i zaloguj ponownie; id powinno wtedy wyświetlić docker na liście grup.

Teraz część szczera, powiedziana wprost: przynależność do grupy docker jest równoznaczna z uprawnieniami root na hoście. Nie "prawie root", nie "podniesione uprawnienia", lecz root. Każdy członek tej grupy może uruchomić docker run --rm -it -v /:/host alpine chroot /host i przejąć kontrolę nad całym systemem plików bez pytania o hasło. Grupa ta istnieje dla wygody, a nie dla izolacji.

Tryb rootless w Docker to realna alternatywa, w której sam demon działa jako użytkownik bez uprawnień. Wiąże się to z kosztami: porty poniżej 1024 wymagają dodatkowej konfiguracji, sieć działa przez warstwę pośredniczącą w przestrzeni użytkownika z mierzalnym narzutem, a niektóre obrazy nie działają poprawnie bez rzeczywistego roota. Na VPS z jednym administratorem, który i tak posiada uprawnienia sudo, zmiana grupy w praktyce niczego nie zmienia i jest to założenie przyjmowane w każdym poradniku tutaj, jednak nigdy nie należy traktować tego dostępu jako czegoś mniej istotnego niż sudo.

Anatomia pliku compose

Każdy stos powinien znajdować się w osobnym katalogu. Nazwa katalogu staje się nazwą projektu, która stanowi prefiks dla kontenerów, sieci i wolumenów:

sudo mkdir -p /opt/miniflux && sudo chown $USER /opt/miniflux && cd /opt/miniflux

Utwórz compose.yml (to współczesna nazwa; docker-compose.yml nadal działa). Pomiń stary klucz version:, jest on przestarzały, a Compose wyświetla ostrzeżenie, jeśli go wykryje.

services:
  miniflux:
    image: miniflux/miniflux:2.2.9
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - DATABASE_URL=postgres://miniflux:${POSTGRES_PASSWORD}@db/miniflux?sslmode=disable
      - RUN_MIGRATIONS=1
      - CREATE_ADMIN=1
      - ADMIN_USERNAME=admin
      - ADMIN_PASSWORD=${ADMIN_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=miniflux
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=miniflux
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "miniflux", "-d", "miniflux"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  db-data:

Każda powyższa linia to konkretna decyzja. Wprowadzaj je pojedynczo.

Przypinanie wersji obrazów, :latest wraz z pull to niekontrolowana aktualizacja

Używaj postgres:16-alpine, a nie postgres:latest. Tag nie jest zamrożony: :latest przy każdym poleceniu pull wskazuje na obraz, który został ostatnio wypchnięty przez twórcę. W połączeniu z nawykiem rutynowej aktualizacji, o którym dowiesz się za chwilę, docker compose pull && docker compose up -d oraz :latest oznaczają, że skoki wersji głównych nastąpią w momencie, gdy udostępni je autor, a nie wtedy, gdy Ty tego chcesz. W przypadku PostgreSQL nie jest to sytuacja hipotetyczna: nagły skok z wersji 16 na 17 sprawi, że kontener wpadnie w pętlę restartów z powodu niekompatybilnego katalogu danych, ponieważ główne aktualizacje Postgres wymagają zrzutu i przywrócenia danych, a nie zwykłego restartu.

Przypnij przynajmniej wersję główną (postgres:16-alpine śledzi wydania poprawkowe 16.x), a aplikacje przypinaj do konkretnego wydania, takiego jak miniflux/miniflux:2.2.9. Sprawdź stronę wydań projektu i użyj wersji aktualnej w momencie tworzenia pliku. Aktualizacja stanie się wtedy celową zmianą w jednej linii, widoczną w git diff.

Publikowanie na 127.0.0.1, ponieważ Docker omija ufw

"127.0.0.1:8080:8080", adres hosta, port hosta, port kontenera. Większość poradników podaje "8080:8080", co jest skrótem dla 0.0.0.0:8080:8080: nasłuchiwanie na każdym interfejsie, w tym na publicznym.

Oto pułapka, w którą wpada niemal każdy. Docker publikuje port, tworząc regułę DNAT, która przepisuje docelowy adres pakietu na wewnętrzny adres IP kontenera przed filtrowaniem. Pakiet trafia więc ścieżką FORWARD i nigdy nie dociera do INPUT, gdzie znajdują się Twoje reguły ufw. sudo ufw deny 8080 zgłasza sukces, ufw status pokazuje, że port jest zablokowany, a usługa mimo to odpowiada całemu Internetowi. Twój firewall nie jest uszkodzony; jest omijany zgodnie z projektem. Dlaczego Docker omija ufw i jak skutecznie filtrować ruch kontenerów omawia ten mechanizm oraz poprawkę DOCKER-USER dla portów, które muszą pozostać publiczne.

Nawyk, który eliminuje ten problem: wiąż publikowane porty z 127.0.0.1, chyba że masz konkretny powód, by tego nie robić, i umieść reverse proxy przed wszystkim, co powinno być dostępne z zewnątrz. To dokładnie to, co buduje przewodnik po reverse proxy Traefik jako kolejny krok po tej stronie: jeden kontener obsługujący porty 80 i 443, który kieruje ruch do pozostałych usług według nazwy hosta, z obsługą TLS. (Przechodzisz ze starszej konfiguracji Traefik v2? Przewodnik migracji z Traefik v2 do v3 omawia zmiany nazw i reguł.)

Po uruchomieniu stosu zweryfikuj powiązanie: sudo ss -tlnp | grep 8080 powinno wskazywać 127.0.0.1:8080, a nie 0.0.0.0:8080 lub *:8080.

Wolumeny nazwane a bind mounts

db-data:/var/lib/postgresql/data to wolumen nazwany: Docker tworzy i zarządza katalogiem w /var/lib/docker/volumes/ i montuje go w kontenerze. Alternatywą jest bind mount, ./data:/var/lib/postgresql/data, który mapuje wybrany przez Ciebie ścieżkę na hoście.

Podział, który sprawdza się w praktyce: wolumeny nazwane dla danych, z którymi pracują wyłącznie kontenery, przede wszystkim bazy danych, ponieważ Docker inicjuje wolumen z uprawnieniami oczekiwanymi przez obraz i uprawnienia plików działają automatycznie. Bind mounts dla plików, z którymi pracujesz z poziomu hosta, plików konfiguracyjnych edytowanych edytorem tekstu, bibliotek multimediów synchronizowanych przez rsync, czy czegokolwiek, czego ścieżka powinna być oczywista. Klasyczna awaria bind mount dotyczy uprawnień: obraz uruchamia proces z określonym UID (obraz Miniflux działa jako UID 65534, postgres:16-alpine obniża uprawnienia do UID 70), Twój katalog na hoście należy do UID 1000, a aplikacja kończy działanie przy starcie z błędem permission denied w logach. Wolumeny nazwane sprawiają, że ta klasa błędów niemal znika, kosztem przechowywania danych w ścieżce zarządzanej przez Dockera, co opisano poniżej.

environment i .env, trzymaj sekrety poza git

Compose uzupełnia ${POSTGRES_PASSWORD} z pliku o nazwie .env znajdującego się obok compose.yml, ale tylko wtedy, gdy Twoja powłoka nie ma już zdefiniowanej tej zmiennej: wartość wyeksportowana w środowisku powłoki ma pierwszeństwo przed plikiem .env. Zapomniana zmienna export POSTGRES_PASSWORD=... z wcześniejszego eksperymentu może więc po cichu nadpisać plik, a docker compose config (poniżej) pokaże, która wartość wygrała. Utwórz plik:

cat > .env <<'EOF'
POSTGRES_PASSWORD=change-me-to-something-long
ADMIN_PASSWORD=change-me-too
EOF
chmod 600 .env
echo ".env" >> .gitignore

Generuj rzeczywiste wartości za pomocą openssl rand -hex 24. Celowo używamy formatu hex, a nie base64: to hasło trafia do ciągu połączeniowego DATABASE_URL, a znaki /, + i =, które generuje base64, psują parsowanie URL. Jest to błąd, który objawia się jako problem z uwierzytelnianiem, a nie błąd składni, co kosztuje cały wieczór pracy. Linia .gitignore powinna znaleźć się w pliku przed pierwszym commitem: plik compose można bezpiecznie publikować i wersjonować, plik .env nigdy nie powinien trafić do repozytorium, a sekret, który znalazł się w historii git, należy zrotować. Jeśli uruchomisz stos z brakującą zmienną, Compose wyświetli głośne ostrzeżenie i kontynuuje pracę z pustym ciągiem znaków, co w przypadku hasła Postgres oznacza niedziałające wdrożenie:

WARN[0000] The "POSTGRES_PASSWORD" variable is not set. Defaulting to a blank string.

docker compose config wyświetla w pełni zinterpolowany plik, co jest najszybszym sposobem na sprawdzenie, co faktycznie otrzymają kontenery; pamiętaj, że wynik zawiera Twoje sekrety.

depends_on nie czeka na nic, chyba że dodasz healthcheck

Samo depends_on: [db] kontroluje jedynie kolejność startu: Compose uruchamia Postgres jako pierwszy, a aplikację chwilę później, podczas gdy Postgres potrzebuje jeszcze kilku sekund na akceptację połączeń. Aplikacja próbuje połączyć się z bazą, nie udaje się to, więc ulega awarii lub ponawia próbę, w zależności od jakości kodu.

Wersja niezawodna to ta użyta w powyższym pliku: usługa db definiuje healthcheck (Postgres dostarcza pg_isready dokładnie w tym celu), a aplikacja deklaruje depends_on wraz z condition: service_healthy. Compose uruchamia bazę danych, odpytuje stan co 10 sekund i uruchamia Miniflux dopiero, gdy test przejdzie pomyślnie. Jeśli baza danych nigdy nie uzyska stanu gotowości (np. złe hasło, uszkodzony wolumen), aplikacja nigdy nie wystartuje, a Compose poinformuje, która zależność zawiodła:

dependency failed to start: container miniflux-db-1 is unhealthy

Ten komunikat wskazuje na docker compose logs db, gdzie znajduje się rzeczywisty błąd.

restart: unless-stopped

restart: unless-stopped w obu usługach oznacza, że kontenery wracają po awarii oraz po restarcie VPS, ale pozostają wyłączone, jeśli celowo wykonałeś docker compose stop. Alternatywa always przywraca kontenery nawet po ręcznym zatrzymaniu, co rzadko jest pożądanym zachowaniem. Bez polityki restartu, aktualizacja jądra i restart o 4 rano po cichu wyłączą Twoje usługi, dopóki ich nie zauważysz.

Codzienne operacje

Wszystkie codzienne czynności sprowadzają się do pięciu poleceń, wykonywanych z poziomu katalogu projektu.

docker compose up -d        # create and start; idempotent, recreates only what changed
docker compose ps           # status, ports, and health of this project's containers
docker compose logs -f miniflux   # follow one service's logs; --tail 100 for recent history
docker compose pull && docker compose up -d   # upgrade to the pinned tags
docker compose down         # stop and remove containers and the network

Polecenie up -d można bezpiecznie uruchamiać wielokrotnie; porównuje ono plik z bieżącym stanem systemu i modyfikuje tylko te usługi, których konfiguracja lub obraz uległy zmianie. Para poleceń aktualizujących pobiera obrazy wskazywane przez przypięte tagi: poprawki w ramach postgres:16-alpine, natomiast w przypadku sztywnych wersji nic się nie zmieni, dopóki nie zostanie edytowany plik, co jest zamierzonym działaniem. Stare obrazy gromadzą się po aktualizacjach; miejsce na dysku można odzyskać za pomocą docker image prune -f.

Teraz polecenie destrukcyjne, o którym należy pamiętać: docker compose down jest bezpieczne, ponieważ kontenery i sieć są tymczasowe, a dane znajdują się w wolumenach. docker compose down -v usuwa również nazwane wolumeny. Oznacza to natychmiastową utratę bazy danych, bez prośby o potwierdzenie i możliwości cofnięcia operacji. Flaga -v służy do usuwania eksperymentalnych środowisk; w przypadku stosu zawierającego rzeczywiste dane, należy traktować ją tak samo jak rm -rf. W /var/lib/docker/volumes/ nie istnieje mechanizm kosza.

Aby uzyskać jednorazowy dostęp do powłoki wewnątrz uruchomionego kontenera: docker compose exec db psql -U miniflux otwiera konsolę bazy danych, a docker compose exec miniflux sh zapewnia dostęp do powłoki wewnątrz aplikacji.

Gdzie faktycznie znajdują się dane

Wolumeny nazwane otrzymują prefiks projektu, więc db-data w katalogu o nazwie miniflux staje się miniflux_db-data:

docker volume ls
docker volume inspect miniflux_db-data

Dane wyjściowe inspect zawierają istotny wiersz:

"Mountpoint": "/var/lib/docker/volumes/miniflux_db-data/_data"

Ten katalog to baza danych, której właścicielem jest root w systemie plików hosta. Przetrwa on down, aktualizacje oraz przebudowy kontenerów. Jest to również dokładnie ten element, który musi zostać uwzględniony w kopiach zapasowych.

Tworzenie kopii zapasowej wolumenu nazwanego

Standardowy schemat polega na użyciu tymczasowego kontenera, który montuje wolumen w trybie tylko do odczytu obok katalogu hosta i archiwizuje dane za pomocą tar:

docker run --rm \
  -v miniflux_db-data:/data:ro \
  -v "$PWD":/backup \
  alpine:3.22 tar czf /backup/miniflux-db-$(date +%F).tar.gz -C /data .

Nie wymaga to instalacji dodatkowego oprogramowania ani pozostawiania uruchomionych procesów. Przywracanie danych jest procesem odwrotnym, polegającym na użyciu tar xzf w nowym, pustym wolumenie z odwróconymi punktami montowania.

Ważna uwaga dotycząca baz danych: archiwizacja katalogu danych działającego Postgres może przechwycić stan w trakcie zapisu, co uniemożliwi poprawne uruchomienie bazy. Należy albo użyć docker compose stop na czas trwania operacji tar, albo, co jest lepszym rozwiązaniem, wykonać zrzut logiczny, który jest spójny z założenia:

docker compose exec -T db pg_dump -U miniflux miniflux | gzip > miniflux-$(date +%F).sql.gz

Flaga -T wyłącza pseudo-terminal, który Compose przydziela domyślnie; przesyłanie wyjścia zrzutu przez TTY może prowadzić do jego uszkodzenia. Należy umieścić jedno z tych poleceń w cron i skopiować wynik poza VPS; kopia zapasowa na tym samym dysku, co chronione dane, jest jedynie duplikatem, a nie kopią zapasową. Przewodnik po Nextcloud zawiera pełną, zaplanowaną procedurę opartą dokładnie na tych dwóch schematach.

Tryby awarii i towarzyszące im komunikaty

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock, użytkownik nie należy jeszcze do grupy docker lub sesja została rozpoczęta przed dodaniem do niej. id wyświetla aktualne grupy; newgrp docker naprawia bieżącą powłokę, a wylogowanie i ponowne zalogowanie aktualizuje przynależność do wszystkich grup.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?, inny problem: sam daemon nie działa. sudo systemctl status docker oraz sudo journalctl -u docker -n 50 wskazują przyczynę. Na serwerze VPS klasyczną przyczyną jest zapełniony dysk, sprawdź df -h /var/lib/docker w pierwszej kolejności.

Bind for 127.0.0.1:8080 failed: port is already allocated, inny kontener już korzysta z tego portu hosta. docker ps pokazuje który; zazwyczaj winny jest zapomniany kontener z eksperymentów sprzed docker run tygodni. Jeśli docker ps jest czyste, port zajmuje proces niezwiązany z Dockerem: sudo ss -tlnp | grep 8080 wskaże jego nazwę.

yaml: line 14: did not find expected key, błąd wcięć w linii o wskazanej nazwie lub tuż powyżej. Pliki Compose to YAML: wymagają wcięć dwuspacjowych, wyłącznie spacji, a znak tabulacji w dowolnym miejscu powoduje błąd krytyczny. docker compose config weryfikuje poprawność pliku bez uruchamiania usług; warto wyrobić sobie nawyk uruchamiania tego polecenia po każdej edycji.

Niespodzianka z ufw nie generuje żadnego błędu, co czyni ją niebezpieczną: wdrożenie przebiega pomyślnie, ufw status wygląda poprawnie, a skanowanie portów z zewnątrz i tak wykrywa bazę danych. Przeczytaj ponownie sekcję dotyczącą portów, sprawdź każdy wpis ports: pod kątem brakującego prefiksu 127.0.0.1: i potwierdź wynik z innej maszyny za pomocą curl http://your-vps-ip:8080; pożądaną odpowiedzią jest connection refused.

W tym miejscu przewodnik po Traefik pozwala przekształcić ten pojedynczy stos w wiele aplikacji za jednym punktem wejścia HTTPS, a co warto hostować samodzielnie w 2026 stanowi listę zakupów do wdrożenia. Gdy uruchomionych jest kilka takich stosów i każdy posiada własny formularz logowania, samodzielnie hostowany serwer SSO, taki jak Authentik pozwala skonsolidować je w ramach jednego konta za tym samym proxy.

Serwer gry, taki jak serwer Minecraft na VPS, to przyjazny pierwszy projekt w Compose do ćwiczeń. Jeśli wolisz uczyć się na czymś, czego używasz codziennie, openGym, samodzielnie hostowany tracker treningów to niewielki stos przypięty do tagu git zamiast tagu obrazu, który wymaga TLS przed rejestracją pierwszego klucza dostępu (passkey). Zdjęcia to zazwyczaj pierwsza rzecz, którą użytkownicy chcą odzyskać z chmury zewnętrznego dostawcy, a porównanie PhotoPrism i Immich pozwala ustalić minimalne wymagania RAM oraz procedurę kopii zapasowej, na którą się decydujesz, zanim przypiszesz wolumen do którejkolwiek z tych usług. Gdy dwie usługi przestaną wystarczać, uruchomienie AFFiNE jako przestrzeni roboczej w stylu Notion wykorzystuje te same wzorce przy czterech kontenerach i stanowi sprawdzian, czy przypięte tagi, healthchecki oraz nazwane wolumeny weszły już w nawyk.

FAQ

Dlaczego otrzymuję błąd "permission denied while trying to connect to the Docker daemon socket"?

Użytkownik nie należy do grupy docker lub został do niej dodany po rozpoczęciu bieżącej sesji (przynależność jest weryfikowana przy logowaniu). Należy wykonać sudo usermod -aG docker $USER, a następnie newgrp docker lub wylogować się i zalogować ponownie, a na koniec potwierdzić za pomocą id. Grupa ta nadaje uprawnienia równoważne z rootem, dlatego należy dodawać do niej tylko użytkowników, którym przyznano by dostęp sudo.

Czy docker compose down usuwa moje dane?

Standardowe docker compose down nie usuwa danych; usuwa kontenery oraz sieć projektu. Wolumeny nazwane pozostają, a kolejne up -d ponownie je podłączy. docker compose down -v to polecenie destrukcyjne: usuwa wolumeny nazwane, co oznacza utratę bazy danych bez potwierdzenia i możliwości cofnięcia operacji. Nigdy nie należy uruchamiać -v na stosie z rzeczywistymi danymi bez posiadania zweryfikowanej kopii zapasowej.

Jaka jest różnica między docker-compose a docker compose?

docker-compose (z łącznikiem) to stary sposób wywoływania Compose. Na starszych serwerach jest to zazwyczaj Compose v1, oryginalne narzędzie w Pythonie, którego wsparcie zostało zakończone. Docker nadal udostępnia samodzielny plik binarny docker-compose, zbudowany na współczesnym kodzie Go, ale oznacza tę metodę jako niezalecaną i wspieraną tylko dla kompatybilności wstecznej, więc na nowych serwerach należy jej unikać. docker compose (z odstępem) to obecna wersja Compose, od v2 wzwyż (od grudnia 2025 oznaczana jako v5), będąca wtyczką Go dla Docker CLI, instalowaną jako docker-compose-plugin z repozytorium apt Docker. Polecenia i pliki YAML są niemal w pełni kompatybilne, więc gdy stary poradnik sugeruje docker-compose up, należy wpisać docker compose up.

Dlaczego mogę połączyć się z kontenerem Docker z Internetu, mimo że ufw blokuje port?

Ponieważ Docker publikuje porty za pomocą reguł DNAT w łańcuchu PREROUTING w iptables, a przekierowane pakiety przechodzą ścieżką FORWARD przez własne łańcuchy Docker, nigdy nie trafiają do łańcucha INPUT, w którym działają reguły ufw. Dlatego ufw deny 8080 nie ma wpływu na opublikowany port kontenera. Problem należy rozwiązać u źródła: publikować porty na 127.0.0.1: i udostępniać usługi przez reverse proxy.

Czy powinienem użyć wolumenu nazwanego czy bind mount?

Wolumeny nazwane należy stosować dla danych, do których dostęp ma tylko kontener, zwłaszcza w przypadku baz danych, ponieważ Docker automatycznie ustawia właściciela zgodnie z wymaganiami obrazu, co zapewnia poprawne uprawnienia. Bind mounty należy stosować dla plików, którymi zarządza się również z poziomu hosta: edytowanych konfiguracji, przesyłanych mediów czy czegokolwiek, gdzie ścieżka musi być jawna. Jeśli kontener nie startuje z błędem permission denied przy użyciu bind mount, pierwszą rzeczą do sprawdzenia jest niezgodność UID między hostem a kontenerem.