Uporaba Docker Compose: compose.yaml s primeri

Linux Branje: 12 min

Docker Compose v datoteki YAML (compose.yaml) opiše, iz katerih vsebnikov je sestavljena aplikacija, in jih z enim samim ukazom zažene, posodobi ali ustavi. Za stran WordPress sta na primer potrebna dva vsebnika (spletni strežnik s PHP in podatkovna baza) skupaj s pripadajočimi nosilci, gesli in omrežjem. Namesto dolgih ukazov docker run je vse to v eni datoteki, ki jo lahko hraniš tudi v sistemu za nadzor različic, in če jo kopiraš na drug strežnik, dobiš isto okolje.

Compose je tipično orodje lastnega KVM VPS: tako lahko drug ob drugem poganjaš več samostojnih aplikacij (na primer WordPress, n8n ali lastno spletno aplikacijo), vsako v svoji mapi, z lastnimi nastavitvami.

Korake smo novembra 2026 preizkusili v sistemu Ubuntu 24.04, z različicama Docker Engine 29.9.3 in Docker Compose 5.7.0, nameščenima iz Dockerjevega repozitorija. Ukazi delujejo enako tudi v AlmaLinux in Debianu.

Namestitev: docker compose ali docker-compose?

Compose je danes razširitev (plugin) Dockerjevega orodja ukazne vrstice, zato je ime ukaza docker compose, s presledkom. Stari docker-compose (z vezajem), napisan v Pythonu, je bila različica 1.x, ki so jo opustili leta 2023; nanjo lahko naletiš v starih opisih. Ukazi in oblika datoteke so skoraj enaki, le vezaj je treba zamenjati s presledkom.

Razširitev Compose se ob namestitvi Dockerja namesti s paketom docker-compose-plugin (glej: Namestitev Dockerja v sisteme Ubuntu, Debian in AlmaLinux). Preverjanje:

docker compose version
Docker Compose version v5.7.0

Po seriji 2.x je številka različice skočila na 5; ukazi in oblika compose.yaml so ostali enaki. Če je odgovor na ukaz docker compose „unknown command”, razširitev manjka: iz Dockerjevega repozitorija namesti paket docker-compose-plugin. V Ubuntuju sistem ob vnosu ukaza docker-compose ponudi paket apt install docker-compose: to je stara različica 1.29, ne nameščaj je.

V primerih v članku je uporabnik član skupine docker, zato so ukazi navedeni brez sudo. Če ni član, pred njih dodaj sudo.

Zgradba projekta

Vsaka aplikacija naj dobi svojo mapo, v njej pa datoteki compose.yaml in .env:

~/wordpress/
├── compose.yaml   # opis storitev
└── .env           # gesla in nastavitve

Compose ime mape uporablja kot ime projekta: imena vsebnikov, nosilcev in omrežij se začnejo z njim (wordpress-db-1, wordpress_db_data). Če želiš drugo ime, na začetek datoteke dodaj vrstico name: sajatnev.

Ime datoteke je po današnjem priporočilu compose.yaml (dobro je tudi compose.yml). Staro ime docker-compose.yml še vedno deluje. Če sta v isti mapi obe, Compose opozori („Found multiple config files”) in uporabi compose.yaml, zato staro preimenuj ali izbriši.

Primer: WordPress in MariaDB

Spodnji compose.yaml zažene stran WordPress in podatkovno bazo MariaDB:

services:
  db:
    image: mariadb:11.8
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wordpress
      MARIADB_PASSWORD: ${DB_PASSWORD:?Nastavi DB_PASSWORD v datoteki .env}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Add meg a DB_ROOT_PASSWORD-ot}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - backend
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

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

volumes:
  db_data:
  wp_data:

networks:
  backend:

Pripadajoča datoteka .env:

DB_PASSWORD=Valtoztassa-meg-1
DB_ROOT_PASSWORD=Valtoztassa-meg-2
WP_PORT=8080

Gesla zamenjaj z dolgimi, naključnimi vrednostmi (na primer openssl rand -base64 24), datoteko pa naj lahko bereš le ti:

chmod 600 .env

Na začetku datoteke ni vrstice version:. Ključa version: "3.8" in podobnih iz starih opisov Compose ne uporablja več; če ostane v datoteki, je v preizkusu dal to opozorilo: „the attribute version is obsolete, it will be ignored”. Mirno ga izbriši.

Kaj pomenijo deli?

  • services: vsebniki. Ime storitve (db, wordpress) je hkrati njeno omrežno ime: WordPress podatkovno bazo doseže z imenom db (WORDPRESS_DB_HOST: db), brez naslova IP.
  • image: uporabljena slika in oznaka. Pri podatkovni bazi določi glavno različico (mariadb:11.8), da posodobitev nepričakovano ne prinese nove glavne različice.
  • restart: unless-stopped: vsebnik se po ustavitvi ali ponovnem zagonu strežnika zažene sam, razen če si ga ustavil ti. V preizkusu sta se po ponovnem zagonu WordPress in MariaDB zagnala sama, vsebnik, prej ustavljen z docker compose stop, pa je ostal ustavljen. Druge možnosti: no (privzeto), always, on-failure.
  • volumes: mesto podatkov. db_data in wp_data sta poimenovana nosilca, ki ju upravlja Docker (pod /var/lib/docker/volumes/). Ostaneta tudi po izbrisu vsebnika, zato se podatki ob posodobitvi ne izgubijo.
  • ports: 127.0.0.1:8080:80 veže vrata 80 vsebnika na vrata 8080 strežnika, vendar le na lokalnem naslovu. Od zunaj tako ni dosegljiv, le prek obratnega posredniškega strežnika (reverse proxy) pred njim, glej spodaj.
  • networks: omrežje backend je lastno omrežje obeh vsebnikov. Brez navedbe omrežja Compose za projekt sam ustvari omrežje default; poimenovano omrežje je uporabno, če želiš več omrežij (glej del o posredniškem strežniku).
  • healthcheck in depends_on: WordPress se zažene šele, ko podatkovna baza že odgovarja (condition: service_healthy). Uradna slika MariaDB vsebuje skripto healthcheck.sh. V preizkusu je ob prvem zagonu podatkovna baza postala healthy po približno 40 sekundah, WordPress pa se je zagnal šele nato.

Vrstni red depends_on velja le za ukaz docker compose up. Ob ponovnem zagonu strežnika Docker vsebnike zažene po pravilu restart in ne čaka na odvisnosti: v preizkusu sta se po ponovnem zagonu oba vsebnika zagnala v isti sekundi. Aplikacija mora zato prenesti, da podatkovna baza prvih nekaj sekund še ni dosegljiva.

Spremenljivke in datoteka .env

Sklice ${...} Compose izpolni iz datoteke .env v mapi projekta (in iz okoljskih spremenljivk ukazne vrstice), še pred zagonom vsebnikov:

Zapis Pomen
${DB_PASSWORD} Vrednost spremenljivke; če ni navedena, prazno besedilo (z opozorilom)
${WP_PORT:-8080} Privzeta vrednost: če je spremenljivka prazna ali manjka, 8080
${DB_PASSWORD:?sporočilo} Obvezna: če manjka, se Compose ustavi s sporočilom o napaki

Obvezna spremenljivka ščiti pred podatkovno bazo, ki bi se zagnala s praznim geslom. V preizkusu Compose brez .env projekta sploh ni zagnal:

error while interpolating services.db.environment.MARIADB_PASSWORD: required variable DB_PASSWORD is missing a value: Nastavi DB_PASSWORD v datoteki .env

Spremenljivka, navedena v ukazni vrstici, je močnejša od datoteke .env: WP_PORT=8090 docker compose up -d uporabi vrata 8090.

Datoteke .env ne zamenjuj s ključem env_file:, ki ga je mogoče navesti pri storitvah. .env pride na mesta ${...} v compose.yaml, env_file: app.env pa tam naštete spremenljivke preda neposredno vsebniku, kot okoljske spremenljivke. Če ima aplikacija veliko nastavitev, je env_file krajši, kot da bi vse naštel pod environment.

Preverjanje in zagon

Pred zagonom preveri datoteko:

cd ~/wordpress
docker compose config -q

Brez napak ne izpiše ničesar. Brez -q pokaže celotno, izpolnjeno nastavitev, skupaj z vrednostmi namesto spremenljivk: tako je vidno, kaj v resnici pride v vsebnike. Pazi, v njej so berljiva tudi gesla, tega izpisa ne kopiraj na forum ali v zahtevek za podporo.

Zagon v ozadju:

docker compose up -d

Ob prvem zagonu Compose prenese slike, ustvari nosilce in omrežje, nato zažene vsebnike. Brez -d teče v ospredju in v terminalu kaže dnevnike vseh vsebnikov; takrat Ctrl+C ustavi vsebnike.

Vsakodnevni ukazi

Vse ukaze zaganjaj v mapi projekta (ali datoteko navedi s stikalom -f ~/wordpress/compose.yaml).

Ukaz Kaj naredi
docker compose ps Vsebniki projekta, njihovo stanje in vrata
docker compose ls Vsi projekti Compose, ki tečejo na računalniku
docker compose logs -f Dnevnik vseh vsebnikov, sproti (izhod: Ctrl+C)
docker compose logs --tail 50 wordpress Zadnjih 50 vrstic storitve
docker compose exec db bash Ukazna vrstica v tekočem vsebniku
docker compose restart wordpress Ponovni zagon storitve
docker compose stop Ustavitev; vsebniki ostanejo
docker compose start Zagon ustavljenih vsebnikov
docker compose down Ustavitev ter izbris vsebnikov in omrežja; nosilci ostanejo
docker compose down -v Kot down, vendar izbriše tudi nosilce, torej podatke

Izpis ps je širok; bistvene stolpce zahtevaš takole:

docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"

Izpis docker compose ps, docker compose ls in docker volume ls: storitev db v stanju healthy, WordPress na vratih 127.0.0.1:8080, pod tem dva poimenovana nosilca

V dnevniku je na začetku vsake vrstice ime vsebnika, ločeno z barvo:

Izpis docker compose logs -f: dnevnik Apache WordPressa in zagonska sporočila MariaDB drug pod drugim, vsak vsebnik v drugi barvi

Nekaj uporabnih primerov exec:

# Ukazna vrstica MariaDB z geslom iz .env
docker compose exec db mariadb -u wordpress -p wordpress

# Različica PHP v vsebniku WordPress
docker compose exec wordpress php -v

Ko ga zaganjaš v skripti ali iz crona, dodaj stikalo -T (docker compose exec -T ...), ker tam ni terminala.

Nevarnost ukaza down -v

docker compose down izbriše le vsebnike in omrežje; naslednji up -d se zažene z istimi nosilci, torej z istimi podatki. down -v pa izbriše tudi poimenovane nosilce projekta, s tem pa tudi podatkovno bazo in naložene datoteke. V preizkusu je nosilec po ukazu takoj izginil („Volume proba_adat Removed”), brez potrditve. Uporabi ga le, če res želiš odstraniti celoten projekt in njegove podatke, prej pa naredi varnostno kopijo.

Posodabljanje

Posodobitev slik sta dva ukaza:

docker compose pull
docker compose up -d

pull prenese najnovejše slike oznak (na primer wordpress:latest, mariadb:11.8), up -d pa znova ustvari le vsebnike, katerih slika ali nastavitev se je spremenila; drugi tečejo naprej. Podatki v nosilcih ostanejo. Enako up -d uveljavi tudi spremembo compose.yaml ali .env: v preizkusu se je po spremembi WP_PORT znova zagnal le vsebnik WordPress, na novih vratih.

Stare slike ostanejo na disku. Občasno jih počisti; kako, in kako preveriš porabo diska, opisuje članek Brisanje slik, vsebnikov in nosilcev Docker.

Pred zamenjavo glavne različice (na primer novejša serija namesto MariaDB 11.8) vedno naredi varnostno kopijo in preberi opis slike na Docker Hubu: del podatkovnih baz ne zna sam preiti na novo glavno različico.

Gradnja lastne slike

Če ima aplikacija lasten Dockerfile, ključ build namesto image pove, od kod naj jo Compose zgradi:

services:
  web:
    build: ./app
    ports:
      - "127.0.0.1:8081:80"
docker compose build        # le gradnja
docker compose up -d --build  # gradnja in zagon

Pozor: navaden docker compose up -d slike ne zgradi znova, če že obstaja. V preizkusu je up -d po spremembi izvorne datoteke še stregel staro vsebino, novo je prevzel šele up -d --build.

Obratni posredniški strežnik: več aplikacij na enem strežniku

V primeru WordPress posluša le na naslovu 127.0.0.1:8080. Vrata 80 in 443 dobi obratni posredniški strežnik (reverse proxy), ki na podlagi domene v zahtevi odloči, kateremu vsebniku jo posreduje, in skrbi tudi za potrdilo SSL. Tak je na primer Nginx Proxy Manager s spletnim vmesnikom, katerega nastavitev predstavlja članek Namestitev Nginx Proxy Manager z Dockerjem.

Če tudi posredniški strežnik teče v Dockerju, vrat sploh ni treba objaviti: dovolj je, da je spletni vsebnik s posredniškim strežnikom v skupnem omrežju. Skupno omrežje ustvari enkrat:

docker network create proxy

Nato v compose.yaml pri WordPressu izpusti del ports in ga dodaj tudi v skupno omrežje:

  wordpress:
    # ... druge nastavitve nespremenjene, brez dela ports
    networks:
      - backend
      - proxy

networks:
  backend:
  proxy:
    external: true

external: true pomeni, da omrežja ne ustvari ta projekt in ga tudi down ne izbriše. Podatkovna baza naj ostane le v omrežju backend: v preizkusu je bilo iz vsebnika posredniškega strežnika mogoče razrešiti imeni wordpress in wordpress-wordpress-1, db pa ne, zato podatkovna baza tudi s strani posredniškega strežnika ni dosegljiva.

Docker in požarni zid

Če vrata navedeš brez naslova ("8080:80"), jih Docker odpre na vseh omrežnih naslovih, z lastnimi pravili požarnega zidu. Ta imajo prednost pred pravili ufw in firewalld, zato so vrata od zunaj lahko dosegljiva, tudi če jih v požarnem zidu nisi dovolil (v našem članku o SQL Server smo to s firewalld tudi preizkusili: SQL Server Express v Linuxu). Zato vsa vrata, ki jih uporablja le strežnik sam ali posredniški strežnik, veži s predpono 127.0.0.1:. Kaj trenutno posluša na naslovu, dosegljivem od zunaj, preveriš po članku Izpis odprtih vrat: ss in lsof; o nastavitvi požarnega zidu govori članek Kako nastavim požarni zid na svojem VPS?.

Varnostne kopije

Compose ne dela varnostnih kopij; vsebino nosilcev moraš shraniti sam. Dve metodi:

Varnostna kopija podatkovne baze med delovanjem

Pri podatkovni bazi je najzanesljivejše lastno orodje za varnostno kopiranje, ki tudi s tekočega strežnika da konsistentno kopijo:

cd ~/wordpress
set -a; . ./.env; set +a
docker compose exec -T db mariadb-dump -u root -p"$DB_ROOT_PASSWORD" --single-transaction wordpress > wordpress-$(date +%F).sql

Obnovitev v prazno podatkovno bazo:

docker compose exec -T db mariadb -u root -p"$DB_ROOT_PASSWORD" wordpress < wordpress-2026-11-08.sql

Varnostna kopija nosilca s tar, s pomožnim vsebnikom

Nosilce z datotekami zapakira kratkoživ pomožni vsebnik, ki dobi nosilec in trenutno mapo. Prej ustavi storitev, ki piše v nosilec, da se datoteke med kopiranjem ne spreminjajo:

cd ~/wordpress
docker compose stop wordpress
docker run --rm -v wordpress_wp_data:/data:ro -v "$PWD":/backup alpine \
  tar czf /backup/wp_data-$(date +%F).tar.gz -C /data .
docker compose start wordpress

Polno ime nosilca je ime projekta in ime nosilca s podčrtajem (wordpress_wp_data); izpiše ga docker volume ls. Lastnik datoteke varnostne kopije bo root, ker pomožni vsebnik teče kot root.

Obnovitev: izbriši stari nosilec, ga s Compose ustvari znova (create ustvari vsebnike in nosilce brez zagona), vanj razpakiraj varnostno kopijo, nato zaženi projekt:

docker compose down
docker volume rm wordpress_wp_data
docker compose create
docker run --rm -v wordpress_wp_data:/data -v "$PWD":/backup:ro alpine \
  tar xzf /backup/wp_data-2026-11-08.tar.gz -C /data
docker compose up -d

V preizkusu so se obnovljene datoteke WordPress vrnile skupaj z lastnikom (www-data), stran pa je po obnovitvi takoj odgovarjala.

Poleg varnostne kopije shrani tudi compose.yaml in datoteko .env, varnostne kopije pa redno kopiraj na drug računalnik (glej: Kopiranje datotek med strežniki). Pri načrtovanem zaganjanju pomaga članek Sintaksa crontab in načrtovana opravila v Linuxu.

Pogoste napake

  • „no configuration file provided: not found”: ukaza nisi zagnal v mapi projekta ali ime datoteke odstopa od običajnih. Pojdi v mapo ali datoteko navedi s stikalom -f.
  • „required variable … is missing a value”: datoteka .env manjka, je v drugi mapi ali v njej manjka spremenljivka. .env mora biti ob compose.yaml.
  • „port is already allocated” ali „address already in use”: vrata na strežniku že uporablja kaj drugega (drug vsebnik ali izvorni spletni strežnik). Izberi druga vrata ali ustavi drugo storitev.
  • „permission denied while trying to connect to the docker API”: uporabnik ni član skupine docker ali se od dodajanja v skupino ni znova prijavil. Uporabi sudo ali se odjavi in znova prijavi.
  • Vsebnik se vedno znova zaganja (stanje Restarting v ps): poglej njegov dnevnik (docker compose logs --tail 50 <storitev>); večinoma je v ozadju manjkajoča spremenljivka, napačno geslo ali napaka dovoljenj. O sistemskem beleženju govori članek Branje dnevniških datotek: journalctl in /var/log.

Povezani članki

Nazaj: Linux – članki (76)