SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-13

Docker na VPS: różnice i najczęstsze problemy

Dowiedz się, dlaczego Docker na VPS omija reguły UFW, powoduje błędy braku pamięci RAM i zapełnia dysk. Poznaj konkretne rozwiązania dla problemów z restartem kontenerów.

Co zmienia się po uruchomieniu Docker na VPS

Docker na serwerze VPS korzysta z tego samego silnika i tych samych obrazów co Docker na laptopie, więc wszystkie znane polecenia działają w ten sam sposób. Zmienia się natomiast otoczenie. Laptop dysponuje zapasem pamięci, firewallem, którego nikt nie skanuje, oraz dyskiem na tyle dużym, że nie wymaga on ciągłego monitorowania. Wynajęty serwer ma sztywny limit pamięci, publiczny adres IP skanowany w ciągu kilku minut od uruchomienia oraz system plików root, który Docker zapełni bez pytania.

Cztery różnice powodują większość problemów na małych maszynach:

  • Pamięć jest ograniczona, a jądro systemu rozwiązuje jej niedobór poprzez zabijanie procesów.
  • Opublikowany port omija UFW (uncomplicated firewall), ponieważ Docker samodzielnie dopisuje własne reguły firewalla.
  • Kontenery nie uruchamiają się ponownie po restarcie systemu, chyba że zostało to wcześniej skonfigurowane.
  • Obrazy, kontenery, wolumeny oraz cache budowania rosną, aż do zapełnienia dysku.

Każda z poniższych sekcji wskazuje przyczynę awarii, komunikat, który faktycznie zobaczysz, oraz szczegółowy przewodnik naprawy. Jeśli nie przygotowałeś jeszcze pliku compose, przeczytaj najpierw Podstawy Docker Compose na VPS i wróć tutaj. Ta strona zakłada, że potrafisz już uruchomić stos usług.

Ile pamięci RAM zużywa kontener Docker?

Mniej, niż zakłada większość użytkowników. Kontener to proces wewnątrz cgroup (control group), a nie maszyna wirtualna, dlatego nie posiada własnego jądra systemu ani sztywno przydzielonej pamięci. Koszt zasobów zależy od tego, co proces wewnątrz kontenera faktycznie wykorzystuje. Dzięki temu pełny stos usług mieści się w 2 GB, podczas gdy ten sam stos uruchomiony na maszynach wirtualnych wymagałby znacznie więcej.

Poniższe wartości to typowe zużycie w stanie spoczynku dla standardowych obrazów na systemie Ubuntu 24.04 przy domyślnej konfiguracji, odczytane z docker stats kilka minut po uruchomieniu. Stanowią one punkt wyjścia do planowania, a nie benchmark obciążenia. Przed poleganiem na jakichkolwiek danych, w tym poniższych, należy uruchomić docker stats --no-stream na własnym serwerze.

ChartTypical idle container memory, stock images, megabytes
The data behind this chart
[
  {
    "label": "nginx",
    "idle_mb": 8,
    "budget_mb": 64
  },
  {
    "label": "Redis 7",
    "idle_mb": 12,
    "budget_mb": 128
  },
  {
    "label": "Traefik v3",
    "idle_mb": 40,
    "budget_mb": 128
  },
  {
    "label": "PostgreSQL 16",
    "idle_mb": 45,
    "budget_mb": 512
  },
  {
    "label": "Uptime Kuma",
    "idle_mb": 95,
    "budget_mb": 256
  },
  {
    "label": "MariaDB 11",
    "idle_mb": 190,
    "budget_mb": 512
  },
  {
    "label": "Nextcloud (Apache)",
    "idle_mb": 210,
    "budget_mb": 768
  }
]

Obie kolumny pełnią inne funkcje. idle_mb to zużycie kontenera w stanie bezczynności. budget_mb to wartość, którą należy zarezerwować podczas planowania, ponieważ rzeczywiste użycie nie ogranicza się do spoczynku. PostgreSQL w stanie bezczynności zużywa około 45 MB, ale wymaga 512 MB, gdy aktywne są połączenia, operacje sortowania i pamięć podręczna. Planuj zasoby w oparciu o kolumnę budżetową. Używaj kolumny bezczynności do debugowania.

Zwróć uwagę na wartości w tych 7 wierszach. nginx w stanie bezczynności zużywa 8 MB, a Nextcloud 210 MB. Proxy działające przed aplikacjami jest niemal bezkosztowe. To baza danych i aplikacja PHP determinują wymagania sprzętowe serwera.

Ostrzeżenie dotyczące docker stats: wartość pamięci uwzględnia pamięć podręczną stron (page cache), którą pobrały odczyty plików samego kontenera, dlatego zużycie rośnie przez pewien czas po starcie, a następnie stabilizuje się. Obserwuj je przez godzinę, zanim stwierdzisz, że występuje wyciek pamięci.

Dobieranie rozmiaru VPS: co mieści się w 2 GB, 4 GB i 8 GB

Najpierw odejmij zasoby zajmowane przez system hosta. Jądro systemu, systemd, journald, sshd oraz Docker daemon korzystają z tej samej pamięci RAM co kontenery, a dockerd wraz z containerd zajmują około 100 MB. Należy również zachować wolną pamięć na page cache oraz na nagłe skoki zapotrzebowania podczas budowania obrazów lub wykonywania zrzutów baz danych.

ChartRAM budget by plan size, megabytes
The data behind this chart
[
  {
    "plan": "2 GB VPS",
    "total_mb": 2048,
    "host_reserve_mb": 768,
    "container_mb": 1280
  },
  {
    "plan": "4 GB VPS",
    "total_mb": 4096,
    "host_reserve_mb": 1024,
    "container_mb": 3072
  },
  {
    "plan": "8 GB VPS",
    "total_mb": 8192,
    "host_reserve_mb": 1536,
    "container_mb": 6656
  }
]

host_reserve_mb obejmuje system operacyjny, Docker daemon oraz zapas pamięci zapewniający responsywność serwera pod obciążeniem. Pozostała część to container_mb i jest to jedyna wartość, którą można realnie wykorzystać. Rezerwa rośnie wraz z planem, od 768 MB w najmniejszej jednostce do 1536 MB w największej, ponieważ większy serwer uruchamia więcej kontenerów, generuje więcej logów i wymaga większego page cache.

Plan 2 GB pozostawia 1280 MB na kontenery. Przeznacz 512 MB z tej puli na PostgreSQL oraz 128 MB na Traefik, a połowa dostępnej pamięci zostanie już wykorzystana. Reszta wystarczy na dwie małe aplikacje po około 256 MB każda. To w pełni funkcjonalny serwer. Nie ma tu jednak miejsca na Nextcloud wraz z klastrem wyszukiwania.

Plan 4 GB pozostawia 3072 MB, co pozwala na jednoczesne uruchomienie bazy danych, reverse proxy, trzech aplikacji oraz kontenera monitorującego. Jest to najmniejszy rozmiar, który warto rozważyć dla istotnych usług, ponieważ wolna pamięć pozwala zamortyzować skutki błędnego wdrożenia.

Plan 8 GB pozostawia 6656 MB z całkowitych 8192 MB, a wąskie gardło zazwyczaj przesuwa się z pamięci na procesor lub przepustowość dysku. Jeśli obliczenia wskazują, że stos technologiczny się nie mieści, należy wybrać wyższy plan zamiast próbować optymalizacji na siłę: ile faktycznie kosztuje VPS wyjaśnia, jaka jest miesięczna wartość dodatkowych gigabajtów.

Dwie zasady pozwalają zachować rzetelność obliczeń. Nałóż limit pamięci na każdą usługę, aby jeden proces nie doprowadził do awarii całego serwera. Pozostaw również część budżetu niewykorzystaną, ponieważ docker compose build oraz pg_dump wymagają pamięci w najmniej odpowiednich momentach. Limity pamięci w Docker Compose zawiera składnię oraz opis typowych pułapek.

Dlaczego kontener kończy działanie z kodem 137?

Przyczyną jest przerwanie procesu przez jądro systemu. Kod 137 to suma 128 i 9, gdzie sygnał 9 oznacza SIGKILL. Kontener zażądał więcej pamięci, niż mu przydzielono, co spowodowało interwencję mechanizmu OOM (Out of Memory) killer.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Należy potwierdzić przyczynę zamiast zgadywać:

docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory"

"OOMKilled": true oznacza, że kontener osiągnął własny limit cgroup, a dziennik jądra wskazuje proces, który został wybrany do zakończenia:

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

Jest to sytuacja korzystna, ponieważ skutki awarii ograniczają się do jednego kontenera. Sytuacja niebezpieczna występuje, gdy kontener nie posiada żadnego limitu. Bez limitu jego górną granicą jest zasób całego serwera, więc wyciek pamięci w jednej usłudze powoduje niedobór zasobów dla hosta, a jądro wybiera ofiarę na podstawie rozmiaru w całym systemie. Wpis w dzienniku traci prefiks Memory cgroup i przyjmuje postać Out of memory: Killed process 2417 (postgres). Wybranym procesem jest często baza danych, podczas gdy kontener z wyciekiem pamięci nadal działa. Dlatego ograniczenie każdej usługi jest ważniejsze niż precyzyjne dobranie wartości pojedynczego limitu.

Swap zmienia czas reakcji, a nie arytmetykę zużycia pamięci. Większość obrazów VPS jest dostarczana bez partycji swap. Należy sprawdzić to za pomocą swapon --show, które nie wyświetli żadnych danych, jeśli swap nie istnieje. Plik wymiany zapewnia jądrze miejsce na przeniesienie nieaktywnych stron pamięci, co daje czas na wykrycie problemu.

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h

free -h powinno teraz wyświetlić niezerową wartość w wierszu Swap. Swap nie zwiększa ilości pamięci RAM. Serwer pod stałym obciążeniem pamięci staje się na tyle wolny, że logowanie przez SSH w celu naprawy może być niemożliwe, dlatego swap należy traktować jako bufor alarmowy i zadbać o odpowiednie rozmiary zasobów.

Dlaczego UFW nie blokuje opublikowanego portu Docker?

Ruch sieciowy nigdy nie dociera do łańcucha, który chroni UFW. Gdy publikujesz port za pomocą -p 5432:5432 lub wpisu ports: w pliku compose, demon zapisuje regułę DNAT (destination network address translation) w tablicy nat oraz regułę accept we własnym łańcuchu DOCKER. Pakiet skierowany do kontenera jest przekazywany bezpośrednio do niego, zamiast zostać dostarczony do hosta. W rezultacie pakiet jest obsługiwany w ścieżce FORWARD i nigdy nie przechodzi przez reguły INPUT zapisane przez UFW.

Możesz zaobserwować to zjawisko na serwerze:

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW może wyświetlać 5432 DENY IN Anywhere, podczas gdy tablica nat zawiera regułę DNAT tcp ... to:172.18.0.2:5432 dla tego samego portu. Z innej maszyny polecenie nc -vz your.server.ip 5432 nadal nawiązuje połączenie. Baza danych jest dostępna z publicznego Internetu, mimo że zapora sieciowa wskazuje, iż jest inaczej.

Rozwiązaniem jest ograniczenie publikacji portów. Kontenery w ramach jednego projektu compose współdzielą sieć i komunikują się ze sobą za pomocą nazw usług. Baza danych, która obsługuje wyłącznie aplikację obok niej, nie wymaga żadnego wpisu ports:. Jeśli potrzebujesz dostępu lokalnego, powiąż publikację z interfejsem loopback:

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

Po wykonaniu docker compose up -d, polecenie nc -vz your.server.ip 5432 z zewnątrz kończy się niepowodzeniem, a psql -h 127.0.0.1 -p 5432 na serwerze nadal działa poprawnie. W poprawnie skonfigurowanym, małym stosie usług, tylko reverse proxy publikuje cokolwiek, na portach 80 i 443. Dlaczego opublikowane porty Docker omijają UFW omawia łańcuch DOCKER-USER w przypadkach, gdy musisz opublikować port i jednocześnie go filtrować, a Podstawy zapory sieciowej UFW opisuje reguły hosta znajdujące się poniżej.

Dlaczego moje kontenery znikają po restarcie?

Ponieważ nie otrzymały polecenia powrotu. Kontener jest tworzony z polityką restartu no, jeśli nie zostanie ona jawnie zdefiniowana, więc po restarcie pozostaje zatrzymany, a daemon nie podejmuje żadnych działań. Restart na serwerze VPS nie jest rzadkością: aktualizacje jądra z mechanizmu unattended upgrades, prace konserwacyjne dostawcy oraz wspomniana wyżej sekwencja OOM kończą się restartem.

Dwa warunki muszą zostać spełnione. Daemon musi uruchamiać się wraz z systemem:

systemctl is-enabled docker

Polecenie to zwraca enabled w standardowej instalacji Ubuntu. Następnie każda usługa musi posiadać odpowiednią politykę:

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

Opcja unless-stopped przywraca kontener po restarcie i respektuje kontenery zatrzymane celowo. Opcja always restartuje również te kontenery, które zostały zatrzymane celowo, w momencie restartu daemona, co może być zaskoczeniem podczas debugowania. Sama edycja pliku nie wystarczy, ponieważ polityka restartu jest ustalana w momencie tworzenia kontenera. Należy wykonać docker compose up -d, aby kontener został utworzony ponownie, a następnie sprawdzić bieżącą wartość:

docker inspect my-app | grep -A3 RestartPolicy

Następnie należy celowo zrestartować serwer i wykonać docker compose ps w katalogu projektu. Stos, który przetrwa planowany restart, przetrwa również nieplanowany. Jeśli stos wymaga gwarancji kolejności uruchamiania lub wykonania zadania jednorazowego przy starcie, lepszym narzędziem jest jednostka systemd: uruchamianie Docker Compose przy starcie zawiera odpowiedni plik jednostki. Aby sprawdzić, czy kontener, który powrócił, rzeczywiście obsługuje ruch, należy dodać kontrole stanu w Compose.

Dlaczego dysk mojego VPS jest pełny?

Docker przechowuje wszystkie dane, dopóki nie otrzyma polecenia ich usunięcia. Każdy pobrany tag obrazu, każdy zatrzymany kontener, każdy anonimowy wolumen pozostawiony po odtworzeniu oraz każda warstwa pamięci podręcznej budowania (build cache) pozostają na dysku. W przypadku systemu plików root o rozmiarze 40 GB lub 80 GB, co jest standardem w tych planach, prowadzi to do awarii w ciągu miesięcy, a nie lat.

Pełny dysk nie wygląda jak awaria systemu. W tej samej godzinie otrzymasz no space left on device z kontenera, z apt, z journald oraz z docker pull. PostgreSQL przestaje przyjmować zapisy. Serwer nadal działa, co utrudnia wykrycie problemu w porównaniu do pętli restartów.

Sprawdź przed usunięciem:

docker system df
df -h /

docker system df dzieli całkowite zużycie na obrazy, kontenery, wolumeny lokalne i pamięć podręczną budowania, z kolumną RECLAIMABLE obok każdej pozycji. Na serwerze, który samodzielnie buduje obrazy, pamięć podręczna budowania jest zazwyczaj największą pozycją.

docker image prune -a
docker builder prune
docker system df

docker image prune -a usuwa każdy obraz, który nie jest używany przez żaden kontener. docker builder prune czyści pamięć podręczną budowania. Obie operacje są bezpieczne podczas działania usług, ponieważ elementy w użyciu są pomijane. Niebezpieczne jest natomiast docker system prune --volumes, które usuwa każdy wolumen niepowiązany obecnie z żadnym kontenerem. Stos (stack), który został zatrzymany na weekend, ma dokładnie taką postać, więc jego wolumen bazy danych zostanie usunięty. Przeczytaj bind mounts versus named volumes przed użyciem tej flagi i najpierw wykonaj kopię zapasową.

Logi kontenerów to mniej oczywiste źródło przyrostu danych. Domyślny sterownik json-file nie posiada limitu rozmiaru, więc jeden "gadatliwy" kontener zapisuje gigabajty w /var/lib/docker/containers. Ogranicz go dla każdego kontenera w /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Zastosuj zmiany za pomocą sudo systemctl restart docker, co spowoduje restart kontenerów, więc wybierz odpowiedni moment. Limit dotyczy kontenerów utworzonych po wprowadzeniu zmiany, dlatego odtwórz działające kontenery za pomocą docker compose up -d --force-recreate i potwierdź:

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

Wynik polecenia inspect powinien pokazywać ustawione max-size. Jeśli pole jest puste, kontener powstał przed wprowadzeniem zmiany i nadal zapisuje logi bez limitu.

Nawyki utrzymujące mały serwer Docker w dobrej kondycji

Żaden z poniższych punktów nie wymaga panelu sterowania ani nauki obsługi dodatkowych narzędzi.

  • Uruchamiaj docker system df oraz df -h / pierwszego dnia każdego miesiąca. Dwie komendy, trzydzieści sekund pracy i widać trendy na długo przed wystąpieniem awarii.
  • Ustal limit pamięci dla każdej usługi, nawet dla tych, które wydają się niewielkie. Limit sprawia, że w razie problemów awarii ulega tylko jeden kontener, a nie cały host.
  • Monitoruj serwer z zewnątrz, aby otrzymywać powiadomienia o wysokim zużyciu pamięci lub braku miejsca na dysku, zanim zareaguje kernel. Uptime Kuma działa w kontenerze i w stanie spoczynku zużywa około 95 MB.
  • Wykonuj kopie zapasowe wolumenów, a nie kontenerów. Kontener jest elementem jednorazowym, wolumen nie. Kopie zapasowe restic na VPS obejmują harmonogram oraz test przywracania danych.
  • Przypinaj tagi obrazów w pliku compose i aktualizuj je w wybranym przez siebie dniu. Przy użyciu latest, wersja pobrana podczas kolejnego docker compose pull będzie tą, która została wydana danego ranka.

Mały VPS z Dockerem pozostaje stabilny przez lata, jeśli cztery parametry mieszczą się w normie: budżet pamięci, lista opublikowanych portów, polityka restartu dla każdej usługi oraz ilość wolnego miejsca na dysku. Wszystko inne to ten sam Docker, którego używasz już w domu.

FAQ

Ile pamięci RAM potrzeba do uruchomienia Docker na VPS?

Sam Docker jest mało wymagający. Demon oraz containerd zajmują łącznie około 100 MB, a reszta zapotrzebowania zależy od uruchomionych kontenerów. Najpierw należy zarezerwować zasoby dla hosta: 768 MB na maszynie o pojemności 2048 MB na system operacyjny, demona oraz zapas pamięci, co pozostawia 1280 MB do wykorzystania. Baza danych przy 512 MB, reverse proxy przy 128 MB oraz dwie małe aplikacje mieszczą się w tym limicie. Należy zmierzyć własny stos technologiczny za pomocą docker stats --no-stream, zamiast polegać na publikowanych szacunkach.

Czy można uruchomić Docker na VPS z 1 GB RAM?

Tak, w przypadku jednego lub dwóch lekkich kontenerów, pod warunkiem dodania pliku wymiany (swap) przed rozpoczęciem pracy. Mniej więcej połowa zasobów maszyny 1 GB jest zajęta przez system operacyjny i demona Docker, co pozostawia miejsce na małą aplikację i reverse proxy, ale nie na bazę danych pod realnym obciążeniem. Budowanie obrazów na maszynie o takiej wielkości zakończy się niepowodzeniem lub spowoduje przerwanie innych procesów, dlatego obrazy należy budować w innym miejscu i pobierać gotowe.

Czy UFW chroni kontener Docker?

Nie w przypadku portów wystawionych na zewnątrz. Docker tworzy własne reguły DNAT oraz forward, więc pakiet skierowany na opublikowany port kontenera jest przekazywany bezpośrednio do niego, zamiast trafiać do hosta, przez co reguły INPUT zarządzane przez UFW go nie widzą. ufw deny 5432 może być aktywny, podczas gdy dany port odpowiada na zapytania z Internetu. Należy publikować porty na interfejsie loopback za pomocą 127.0.0.1:5432:5432, pozostawiać usługi wewnętrzne nieopublikowane lub filtrować ruch w łańcuchu DOCKER-USER.

Czy kontenery zrestartują się po ponownym uruchomieniu VPS?

Tylko jeśli zostały utworzone z odpowiednią polityką restartu. Należy ustawić restart: unless-stopped dla każdej usługi, wykonać docker compose up -d, aby kontenery zostały zaktualizowane z tą konfiguracją, oraz potwierdzić, że systemctl is-enabled docker zwraca enabled. Następnie należy celowo zrestartować serwer i sprawdzić docker compose ps. Polityka restartu, która nie została przetestowana, nie jest polityką restartu.

Jak często należy czyścić obrazy Docker?

W przypadku większości małych serwerów wystarczy raz w miesiącu lub wtedy, gdy docker system df wskazuje ilość miejsca, którą warto odzyskać. docker image prune -a oraz docker builder prune są bezpieczne podczas działania usług, ponieważ używane obrazy i pamięć podręczna są pomijane. Należy unikać docker system prune --volumes, chyba że użytkownik ma pewność, które wolumeny nie są używane, ponieważ polecenie to usuwa dane wszystkich zatrzymanych stosów aplikacji.