Przejdź do treści
Aktualny kupon:10% zniżkiUżyj w zamówieniu

Docker i Docker Compose na VPS: instalacja i pierwsza aplikacja

Zainstaluj Dockera z oficjalnego repozytorium, uruchom WordPress z MariaDB przez Docker Compose i naucz się zarządzać kontenerami, aktualizować je, archiwizować i zabezpieczać firewallem.

  • zaawansowany
  • ~25 min
  • Zaktualizowano 9.10.2026

Docker uruchamia aplikacje w izolowanych kontenerach, które niosą ze sobą wszystko, czego potrzebują. Zainstalujemy go z oficjalnego repozytorium, przez Docker Compose uruchomimy WordPress z bazą danych i przejdziemy przez zarządzanie, aktualizacje, firewall i kopie zapasowe.

Czego potrzebujesz

  • VPS w StormHost z Ubuntu 24.04 lub Debianem 13
  • Połączenie SSH jako root (poradnik)
  • Dla WordPressa z HTTPS domena skierowana na VPS
  • Około 25 minut

Jak to działa#

PojęcieCo to jest
ObrazGotowy szablon aplikacji: biblioteki systemowe, program i jego ustawienia. Pobierany z rejestru, najczęściej Docker Hub (na przykład wordpress, mariadb).
KontenerDziałająca instancja obrazu. Ma własny system plików i sieć, ale dzieli jądro z VPS. Kontener możesz w dowolnej chwili usunąć i utworzyć ponownie z tego samego obrazu.
WolumenTrwałe miejsce na dane poza kontenerem. Dane w wolumenie przetrwają usunięcie i aktualizację kontenera. Wszystko, co chcesz zachować, musi być w wolumenie.
PortKontener ma własną sieć. Aby był dostępny z zewnątrz, jego port jest „publikowany” na porcie VPS.
Docker ComposeNarzędzie uruchamiające całą wielokontenerową aplikację (na przykład stronę i bazę danych) z jednego pliku compose.yaml.

Instalacja#

Zainstaluj Dockera z oficjalnego repozytorium Dockera. Pakiet docker.io z repozytorium dystrybucji bywa starszy i koliduje z oficjalnymi pakietami.

Najpierw usuń ewentualne kolidujące pakiety. Na świeżym VPS polecenie niczego nie znajdzie, co jest w porządku.

Ubuntu 24.04

bash
apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

Debian 13

bash
apt remove $(dpkg --get-selections docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc | cut -f1)
  • dpkg --get-selections … – wypisuje, które z podanych pakietów są zainstalowane.
  • | cut -f1 – zostawia tylko nazwy pakietów.
  • apt remove $(…) – odinstalowuje je. Dane w /var/lib/docker zostają.

Następnie dodaj repozytorium Dockera i zainstaluj Dockera:

Ubuntu 24.04

bash
apt update
apt -y install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Debian 13

bash
apt update
apt -y install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Co robi każda linia:

PolecenieCo robi
apt -y install ca-certificates curlCertyfikaty dla HTTPS i narzędzie do pobierania.
install -m 0755 -d /etc/apt/keyringsTworzy katalog na klucze podpisujące repozytoriów z uprawnieniami 0755.
curl -fsSL …/gpg -o /etc/apt/keyrings/docker.ascPobiera klucz podpisujący Dockera. -f = błąd przy błędach HTTP, -s = cicho, -S = mimo to pokazuj błędy, -L = podążaj za przekierowaniami.
chmod a+r …/docker.ascKażdy może czytać klucz (apt czyta go jako inny użytkownik).
tee /etc/apt/sources.list.d/docker.sources <<EOF … EOFZapisuje definicję repozytorium do pliku.
Types: debRepozytorium z pakietami binarnymi.
URIs:Adres repozytorium dla Twojego systemu.
Suites: $(…)Nazwa kodowa systemu (noble dla Ubuntu 24.04, trixie dla Debiana 13), wypełniana automatycznie.
Components: stableTylko wydania stabilne.
Architectures: $(dpkg --print-architecture)Architektura CPU (amd64).
Signed-By:Pakiety muszą być podpisane tym kluczem, inaczej apt je odrzuca.
apt -y install docker-ce …Instaluje Docker Engine (docker-ce), wiersz poleceń (docker-ce-cli), środowisko kontenerów (containerd.io), budowanie obrazów (docker-buildx-plugin) i Compose (docker-compose-plugin).

Po instalacji Docker startuje sam i jest włączony przy uruchomieniu VPS. Sprawdź instalację:

bash
docker run --rm hello-world
docker compose version
  • docker run --rm hello-world – pobiera obraz testowy i go uruchamia; --rm usuwa kontener po zakończeniu. Zobaczysz Hello from Docker!.
  • docker compose version – wypisuje wersję Compose, na przykład Docker Compose version v5.…. Po wersji 2 Compose przeskoczył od razu na 5, więc starsze instalacje pokazują v2.…. Polecenia w tym poradniku działają z obiema.

Grupa docker = uprawnienia roota

Każdy w grupie docker może uruchamiać kontenery z dostępem do całego dysku VPS, co w praktyce oznacza uprawnienia roota. Dodawaj tylko osoby, którym dałbyś także roota. Jeśli pracujesz jako root, grupy nie potrzebujesz.

Pierwsza aplikacja: WordPress z MariaDB#

Projekt i hasła#

  1. Utwórz katalog projektu i przejdź do niego:

    bash
    mkdir -p /opt/wordpress
    cd /opt/wordpress

    Nazwa katalogu (wordpress) staje się nazwą projektu Compose i prefiksem nazw kontenerów i wolumenów.

  2. Utwórz plik z hasłami przez nano .env. Hasła wygeneruj generatorem haseł (tylko litery i cyfry):

    /opt/wordpress/.envini
    DB_PASSWORD=<HASLO_BAZY_DANYCH>
    DB_ROOT_PASSWORD=<HASLO_ROOT_BAZY_DANYCH>
    • DB_PASSWORD – hasło użytkownika bazy danych WordPressa.
    • DB_ROOT_PASSWORD – hasło użytkownika root wewnątrz kontenera bazy danych (do kopii zapasowych i administracji).
  3. Ustaw plik jako czytelny tylko dla roota:

    bash
    chmod 600 .env

Plik compose.yaml#

Utwórz nano compose.yaml z tą zawartością:

/opt/wordpress/compose.yamlYAML
services:
  db:
    image: mariadb:lts
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wordpress
      MARIADB_PASSWORD: ${DB_PASSWORD}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      start_period: 10s
      interval: 10s
      timeout: 5s
      retries: 3

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html

volumes:
  db_data:
  wp_data:

Linia po linii:

LiniaCo robi
services:Lista kontenerów aplikacji. Nazwa usługi (db, wordpress) służy też jako jej nazwa hosta w sieci projektu.
image: mariadb:ltsOficjalny obraz MariaDB; tag lts = najnowsze wydanie z długim wsparciem.
restart: unless-stoppedKontener startuje ponownie po awarii i po restarcie VPS, chyba że zatrzymano go ręcznie.
MARIADB_DATABASE, MARIADB_USER, MARIADB_PASSWORDPrzy pierwszym starcie tworzy bazę danych i użytkownika z hasłem.
MARIADB_ROOT_PASSWORDHasło roota bazy danych, ustawiane przy pierwszym starcie.
${DB_PASSWORD}Wartość jest uzupełniana z pliku .env w tym samym katalogu, więc hasła nie są wpisane bezpośrednio w compose.yaml.
volumes: - db_data:/var/lib/mysqlDane bazy są przechowywane w wolumenie db_data, więc przetrwają aktualizacje kontenera.
healthcheck:Co 10 s Docker skryptem healthcheck.sh z obrazu MariaDB sprawdza, czy baza przyjmuje połączenia i jest zainicjalizowana.
depends_on: db: condition: service_healthyWordPress startuje dopiero, gdy baza danych jest gotowa.
image: wordpress:latestOficjalny obraz WordPressa (Apache + PHP).
ports: - "127.0.0.1:8080:80"Port 80 kontenera jest dostępny na porcie 8080 VPS, ale tylko z samego VPS (127.0.0.1). Z zewnątrz dostępny tylko przez Nginx.
WORDPRESS_DB_HOST: dbAdres bazy danych: nazwa usługi db w sieci projektu.
WORDPRESS_DB_USER, …_PASSWORD, …_NAMEDane logowania do bazy; muszą zgadzać się z wartościami usługi db.
volumes: - wp_data:/var/www/htmlPliki WordPressa (pluginy, motywy, przesłane obrazy) są w wolumenie wp_data.
volumes: na końcuDeklaruje nazwane wolumeny używane przez projekt.

Uruchomienie#

bash
docker compose up -d
docker compose ps
  • docker compose up -d – pobiera obrazy, tworzy sieć, wolumeny i kontenery i uruchamia je w tle (-d, detached).
  • docker compose ps – wypisuje kontenery projektu i ich status. db pokazuje healthy, wordpress pokazuje Up.

Kontrola z VPS: curl -I http://127.0.0.1:8080 zwraca nagłówki odpowiedzi WordPressa (przekierowanie do instalatora).

Zmienne MARIADB_* działają tylko przy pierwszym starcie

Baza danych powstaje przy pierwszym starcie z pustym wolumenem. Późniejsza zmiana hasła w .env nie zmienia go w bazie. Zmień hasło w bazie przez ALTER USER (zobacz MariaDB na VPS) i dopiero potem w .env.

Udostępnienie przez Nginx i HTTPS#

WordPress nasłuchuje teraz tylko na 127.0.0.1:8080. Udostępnij go przez Nginx jako reverse proxy według poradnika Nginx i HTTPS: w konfiguracji użyj proxy_pass http://127.0.0.1:8080; i swojej domeny, na przykład blog.example.cz, a potem uzyskaj certyfikat przez certbot --nginx -d blog.example.cz.

Obraz WordPressa sam wykrywa HTTPS z nagłówka X-Forwarded-Proto, który Nginx wysyła po skonfigurowaniu jak w tamtym poradniku. Na koniec otwórz https://blog.example.cz i dokończ instalację WordPressa w przeglądarce.

Zarządzanie kontenerami#

Polecenia docker compose uruchamiaj w katalogu projektu (cd /opt/wordpress).

PolecenieCo robi
docker compose psStatus kontenerów projektu.
docker compose logs -fLogi wszystkich usług; -f = śledzenie na żywo (zatrzymaj przez Ctrl+C). Tylko jedna usługa: docker compose logs -f wordpress.
docker compose restartRestartuje kontenery.
docker compose stop / startZatrzymuje / uruchamia kontenery bez usuwania czegokolwiek.
docker compose up -dPo zmianie compose.yaml odtwarza tylko zmienione kontenery.
docker compose downZatrzymuje i usuwa kontenery i sieć. Wolumeny z danymi zostają.
docker compose exec db mariadb -uroot -pWykonuje polecenie wewnątrz działającego kontenera (tutaj konsola MariaDB).
docker compose configSprawdza compose.yaml i wypisuje go z uzupełnionymi wartościami z .env (uwaga, pokazuje też hasła).

Aktualizacja do nowych wersji obrazów:

bash
docker compose pull
docker compose up -d
docker image prune -f
  • docker compose pull – pobiera nowe wersje obrazów.
  • docker compose up -d – odtwarza kontenery, które mają nowszy obraz. Dane w wolumenach zostają.
  • docker image prune -f – usuwa stare, nieużywane już obrazy; -f pomija potwierdzenie.

docker compose down -v usuwa dane

Flaga -v polecenia docker compose down usuwa także wolumeny projektu, czyli bazę danych i pliki WordPressa. Nie da się tego cofnąć. Używaj jej tylko wtedy, gdy chcesz całkowicie usunąć aplikację i masz kopię zapasową.

Opcje restart::

WartośćKiedy kontener startuje ponownie
noNigdy (domyślnie).
alwaysZawsze, nawet po ręcznym zatrzymaniu (po restarcie Dockera lub VPS).
on-failureTylko gdy kończy się błędem.
unless-stoppedZawsze, poza przypadkiem, gdy zatrzymano go ręcznie. Zalecane dla usług.

Porty i firewall#

Docker publikuje porty kontenerów własnymi regułami firewalla, które omijają UFW. Port opublikowany jako 8080:80 jest dostępny z internetu, nawet jeśli nie zezwolisz na niego w UFW. Szczegółowo wyjaśnia to poradnik Firewall UFW.

Wpis w ports:Kto może dotrzeć do portu
"8080:80"Każdy w internecie, niezależnie od UFW.
"127.0.0.1:8080:80"Tylko procesy na VPS (na przykład Nginx). Zalecane dla stron za Nginx.
brak ports:Tylko inne kontenery w tej samej sieci projektu (zwykle bazy danych).

Baz danych i innych usług wewnętrznych w ogóle nie publikuj. Porty, które muszą być publiczne (na przykład serwer gier w kontenerze), ogranicz firewallem w strefie klienta (poradnik). Znajduje się przed VPS i Docker nie może go ominąć.

Zwalnianie miejsca na dysku#

Obrazy, zatrzymane kontenery i logi zajmują z czasem dużo miejsca.

bash
docker system df
docker system prune
  • docker system df – ile miejsca zajmują obrazy, kontenery, wolumeny i cache.
  • docker system prune – po potwierdzeniu usuwa zatrzymane kontenery, nieużywane sieci, obrazy bez tagu i cache budowania. Nie usuwa wolumenów z danymi, chyba że dodasz --volumes.

Ostrożnie z usuwaniem wolumenów i z -a

docker system prune --volumes usuwa także nieużywane wolumeny anonimowe (bez nazwy, tworzone na przykład przez docker run bez -v). Jeśli trzymały dane kontenera, który jest właśnie usunięty lub zatrzymany, te dane przepadają. docker volume prune -a usuwa też wolumeny nazwane, jak wordpress_db_data, gdy nie używa ich żaden kontener, na przykład po docker compose down. docker system prune -a usuwa wszystkie obrazy, których nie używa żaden kontener. Przed potwierdzeniem przeczytaj, co polecenie zamierza usunąć.

Domyślnie logi kontenerów nie są rotowane i mogą zapełnić dysk. Ustaw sterownik local, który sam usuwa stare logi (domyślnie trzyma najwyżej 5 plików po 20 MB na kontener). Utwórz plik nano /etc/docker/daemon.json z poniższą zawartością. Jeśli plik już istnieje, nie nadpisuj go; dodaj tylko linię "log-driver" (wpisy oddzielone przecinkami):

/etc/docker/daemon.jsonJSON
{
  "log-driver": "local"
}

Następnie zrestartuj Dockera (systemctl restart docker) i odtwórz kontenery (docker compose up -d --force-recreate). Ustawienie dotyczy tylko nowo tworzonych kontenerów.

Kopie zapasowe#

Archiwizuj wolumeny, nie kontenery. Kontener odtworzysz z obrazu, ale danych nie.

  1. Utwórz katalog na kopie zapasowe: mkdir -p /root/zalohy.

  2. Zarchiwizuj bazę danych eksportem (bardziej spójne niż kopiowanie plików):

    bash
    cd /opt/wordpress
    docker compose exec -T db sh -c 'exec mariadb-dump --single-transaction --all-databases -uroot -p"$MARIADB_ROOT_PASSWORD"' > /root/zalohy/wp-db-$(date +%F).sql
    • docker compose exec -T db – wykonuje polecenie w kontenerze db; -T wyłącza terminal, aby wyjście dało się przekierować do pliku.
    • sh -c '…' – polecenie działa w powłoce kontenera, gdzie ustawiona jest zmienna $MARIADB_ROOT_PASSWORD.
    • mariadb-dump --single-transaction --all-databases – eksportuje wszystkie bazy bez blokowania.
    • > /root/zalohy/… – wyjście zapisuje się na VPS, poza kontenerem.
  3. Zarchiwizuj pliki WordPressa, pakując wolumen:

    bash
    docker run --rm -v wordpress_wp_data:/data:ro -v /root/zalohy:/backup alpine tar czf /backup/wp-files-$(date +%F).tar.gz -C /data .
    • docker run --rm – uruchamia jednorazowy kontener, usuwany po zakończeniu.
    • -v wordpress_wp_data:/data:ro – montuje wolumen wp_data projektu wordpress tylko do odczytu (ro).
    • -v /root/zalohy:/backup – montuje katalog kopii zapasowych z VPS.
    • alpine tar czf … -C /data . – pakuje zawartość wolumenu do archiwum przy użyciu małego obrazu Alpine.

    docker volume ls wypisuje dokładne nazwy wolumenów.

Automatyczne kopie zapasowe przez cron i kopiowanie ich poza VPS opisuje artykuł Kopie zapasowe na VPS.

Weryfikacja#

  • docker compose ps pokazuje oba kontenery jako działające, a bazę danych jako healthy.
  • https://blog.example.cz pokazuje WordPressa z kłódką w pasku adresu.
  • Po restarcie VPS (reboot) kontenery startują same.
  • Z innego komputera nie da się połączyć z http://<ADRES_IP_VPS>:8080 (port jest przypięty tylko do 127.0.0.1).

Rozwiązywanie problemów#

permission denied while trying to connect to the Docker daemon socket#

Nie jesteś rootem, a Twój użytkownik nie jest w grupie docker. Uruchom polecenie z sudo albo zaloguj się jako root.

Bind for 127.0.0.1:8080 failed: port is already allocated lub address already in use#

Port 8080 zajmuje już inny kontener (pierwszy komunikat) albo inny program na VPS (drugi komunikat). Ustal który (ss -tlnp | grep 8080, docker ps) i wybierz inny port w compose.yaml, na przykład 127.0.0.1:8081:80. Potem zaktualizuj także proxy_pass w Nginx.

WordPress pokazuje „Error establishing a database connection”#

Baza danych nie jest jeszcze gotowa albo dane logowania się nie zgadzają. Sprawdź log bazy (docker compose logs db). Jeśli zmieniono hasło w .env po pierwszym starcie, baza nadal ma stare hasło (zobacz ostrzeżenie w sekcji Uruchomienie).

Kontener stale pokazuje Restarting#

Aplikacja w kontenerze zawiesza się zaraz po starcie. Przyczynę znajdziesz w logu: docker compose logs --tail 50 <NAZWA_USLUGI>.

toomanyrequests: You have reached your unauthenticated pull rate limit#

Docker Hub ogranicza anonimowe pobieranie obrazów do 100 na 6 godzin na adres IP. Poczekaj albo zaloguj się kontem Docker Hub: docker login.

no space left on device#

Dysk jest pełny. Sprawdź, co go zajmuje (docker system df, df -h), i posprzątaj zgodnie z sekcją Zwalnianie miejsca na dysku. Jeśli miejsca naprawdę brakuje, zwiększ RAM w zakładce Zmiana pakietu; rozmiar dysku rośnie razem z pamięcią.

Podsumowanie

  • Dockera instaluj z oficjalnego repozytorium download.docker.com, nie z pakietu docker.io.
  • Dane należą do wolumenów; kontenery można w dowolnej chwili usuwać i odtwarzać.
  • Strony publikuj na 127.0.0.1 i udostępniaj przez Nginx z HTTPS; inaczej Docker omija UFW.
  • Aktualizacje: docker compose pull i docker compose up -d.
  • docker compose down -v i prune --volumes usuwają dane; regularnie archiwizuj wolumeny.

Czy ten poradnik był pomocny?

Dotyczy

  • Ubuntu 24.04
  • Debian 13
  • Docker Engine
  • Docker Compose v2 i nowsze

Usługi

Zgłoś błąd w poradniku

Wyszukiwanie w poradnikach