Amb el fitxer ja escrit, toca dominar l'eina que l'interpreta. La CLI de Compose té uns vint-i-cinc subcomandes, però el repartiment és molt desigual: en faràs servir cinc cada dia, unes altres cinc sovint i la resta en situacions concretes.

Aquesta lliçó les recorre per famílies, sempre sobre el compose.yaml d'Aurora Libros de la lliçó anterior. Para especial atenció a dues parelles que es confonen sense parar: up enfront de start, i exec enfront de run.

Contingut

  1. Taula mestra de subcomandes
  2. up: la comanda que reconcilia
  3. down i el perill de -v
  4. start, stop, restart, pause, create
  5. Observació: ps, logs, top, stats, events
  6. Execució puntual: exec enfront de run
  7. Construcció i publicació: build, pull, push
  8. Escalat local amb --scale i els seus límits
  9. Diagnòstic: config i validació a CI
  10. Selecció de fitxers i projecte: -f, -p, COMPOSE_FILE
  11. Autocompletat i àlies útils

  1. Taula mestra de subcomandes

Família Comanda Què fa
Cicle de vida up Crea o actualitza i arrenca tot el que està declarat
down Atura i elimina contenidors i xarxes del projecte
create Crea els contenidors sense arrencar-los
start / stop Arrenca o atura contenidors ja existents
restart Atura i torna a arrencar sense rellegir el fitxer
pause / unpause Congela i descongela els processos (SIGSTOP)
kill Envia un senyal (SIGKILL per defecte)
rm Elimina contenidors aturats
Observació ps Estat dels serveis del projecte
logs Registres agregats per servei
top Processos dins de cada contenidor
stats Consum de recursos en viu
events Flux d'esdeveniments del projecte
port Port del host associat a un port intern
Execució exec Comanda en un contenidor en marxa
run Comanda en un contenidor nou
Imatges build Construeix els serveis amb build:
pull / push Descarrega o publica les imatges
images Imatges que fa servir el projecte
Diagnòstic config Valida i mostra la configuració final
version Versió de Compose
ls Llista tots els projectes de la màquina
cp Copia fitxers entre host i servei
wait Bloqueja fins que un servei acabi

Una comanda que s'oblida i és utilíssima: docker compose ls funciona des de qualsevol directori i et diu quins projectes hi ha aixecats a la màquina i amb quin fitxer.

docker compose ls
NAME            STATUS       CONFIG FILES
aurora-libros   running(4)   /home/joan/aurora-libros/compose.yaml

  1. up: la comanda que reconcilia

docker compose up és el 80 % de l'ús diari. No és "arrencar": és portar l'estat real a l'estat declarat. A cada execució, per a cada servei, Compose calcula un resum de la seva configuració (el config-hash que vas veure a la lliçó 04-01) i decideix:

Situació Acció de Compose
El contenidor no existeix El crea i l'arrenca
Existeix i la configuració coincideix No el toca
Existeix però ha canviat el fitxer o la imatge El recrea
Existeix però està aturat L'arrenca
Existeix i no és al fitxer El deixa (llevat de --remove-orphans)

Aquesta selectivitat és el que fa que editar la memòria de la memòria cau i executar up -d recreï només la memòria cau i no tiri a terra la base de dades.

Opció Efecte
-d, --detach En segon pla. A la pràctica, sempre
--build Construeix abans d'arrencar els serveis amb build:
--no-build Falla si falta una imatge en comptes de construir-la
--pull always Descarrega la imatge encara que existeixi en local
--force-recreate Recrea tot encara que res no hagi canviat
--no-recreate No recrea res encara que hagi canviat
--no-deps Ignora depends_on: només el servei demanat
--wait Espera que els serveis estiguin healthy abans de retornar el control
--wait-timeout 60 Segons màxims d'espera de --wait
--remove-orphans Elimina contenidors del projecte que ja no són al fitxer
--abort-on-container-exit Atura tot si un contenidor acaba (útil a CI)
--scale s=N Arrenca N rèpliques del servei s
cd ~/aurora-libros
docker compose up -d --build --wait
[+] Building 2/2
[+] Running 4/4
 ✔ Container aurora-libros-aurora-db-1     Healthy
 ✔ Container aurora-libros-aurora-cache-1  Healthy
 ✔ Container aurora-libros-aurora-api-1    Healthy
 ✔ Container aurora-libros-aurora-web-1    Healthy

--wait és l'opció que converteix Compose en una eina d'automatització: sense ella, la comanda retorna el control tan bon punt els contenidors estan arrencats, i un script de CI que llança proves immediatament després s'estavella contra una base de dades que encara no accepta connexions. Amb --wait, Compose no retorna el control fins que totes les sondes de salut passen, i surt amb codi diferent de zero si alguna no ho aconsegueix.

Sense -d, up deixa el terminal enganxat als logs agregats de tots els serveis i Ctrl+C atura la pila sencera. És còmode per depurar una arrencada; perillós si te'n descuides en una sessió SSH.

Un cas molt útil de --no-deps: recrear només l'API sense reiniciar la base de dades.

docker compose up -d --no-deps --force-recreate aurora-api

  1. down i el perill de -v

down és la inversa d'up: atura i elimina contenidors i xarxes del projecte.

Comanda Contenidors Xarxes Volums amb nom Volums anònims Imatges
down Elimina Elimina Conserva Elimina Conserva
down -v Elimina Elimina ESBORRA Elimina Conserva
down --rmi local Elimina Elimina Conserva Elimina Esborra les que no tenen etiqueta pròpia
down --rmi all Elimina Elimina Conserva Elimina Esborra totes les del projecte
down --remove-orphans Elimina també els orfes Elimina Conserva Elimina Conserva
stop Conserva (aturats) Conserva Conserva Conserva Conserva

Grava't la segona fila. docker compose down -v esborra els volums amb nom del projecte sense preguntar: a Aurora Libros, això és el catàleg sencer de la llibreria. És exactament el que vols en reiniciar un entorn de desenvolupament i exactament el que arruïna un servidor si el teclejes per inèrcia. Dues defenses: declarar els volums crítics com a external: true (lliçó 04-02) i no crear mai un àlies de shell que inclogui -v.

docker compose down --timeout 30    # marge d'aturada ordenada, per servei

  1. start, stop, restart, pause, create

Comanda Llegeix el fitxer? Recrea? Quan fer-la servir
up -d Si hi ha canvis Sempre que toquis el compose.yaml
start No Mai Tornar a arrencar allò ja creat
stop No No Aturar sense destruir
restart No No Reiniciar el procés, sense aplicar canvis
pause No No Congelar processos alliberant CPU, no memòria
create Si hi ha canvis Crear sense arrencar

El parany de restart mereix un avís explícit: no rellegeix el fitxer. Si canvies una variable d'entorn i executes docker compose restart aurora-api, el contenidor es reinicia amb la configuració antiga i et tornes boig buscant per què no s'aplica el canvi. Per aplicar canvis del fitxer, sempre up -d.

docker compose stop aurora-cache      # aturar un servei concret
docker compose start aurora-cache     # tornar a arrencar-lo
docker compose restart aurora-web     # reinici ràpid de nginx
docker compose kill -s SIGHUP aurora-web   # recarregar configuració sense reiniciar

  1. Observació: ps, logs, top, stats, events

docker compose ps
docker compose ps -a                 # inclou els aturats i els que van acabar
docker compose ps --services         # només noms de servei, un per línia
docker compose ps --status running
docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
docker compose ps --format json | jq -r '.[] | "\(.Service): \(.Health)"'
aurora-api: healthy
aurora-cache: healthy
aurora-db: healthy
aurora-web: healthy

--services és or per als scripts: for s in $(docker compose ps --services); do ...; done.

Els logs són l'eina que més faràs servir:

docker compose logs                       # tots els serveis, agregats
docker compose logs -f aurora-api         # seguir-ne un en temps real
docker compose logs --tail 50 aurora-db   # últimes 50 línies
docker compose logs --since 10m           # dels últims 10 minuts
docker compose logs -t --no-color api web # amb marca de temps, diversos serveis

Compose acoloreix i prefixa cada línia amb el nom del servei, així que docker compose logs -f amb la pila sencera et deixa veure el flux d'una petició travessant web → api → db. Recorda de la lliçó 03-04 que només veuràs allò que els processos escriguin a stdout/stderr.

docker compose top aurora-db     # processos dins del contenidor
docker compose stats --no-stream # consum puntual de tots els serveis
docker compose events --json     # flux d'esdeveniments del projecte
docker compose port aurora-web 80
0.0.0.0:8080

port respon a "en quin port del host està publicat el 80 de la web?", i és imprescindible quan deixes que Docker assigni ports aleatoris.

  1. Execució puntual: exec enfront de run

És la distinció que més confusió genera, i la resposta és simple: exec entra en un contenidor que ja està corrent; run en crea un de nou.

Aspecte exec run
Contenidor L'existent del servei Un de nou, amb sufix -run-<hash>
Requisit El servei ha d'estar en marxa El servei no cal que estigui arrencat
Dependències Irrellevant Arrenca les de depends_on (evita-ho amb --no-deps)
Ports Els que ja té No publica els ports (llevat de --service-ports)
En acabar El contenidor continua viu Queda aturat, llevat de --rm
Ús típic Inspeccionar, psql, redis-cli, shell Tasques puntuals: migracions, proves, seeds
# EXEC: parlar amb la base de dades que està servint ara mateix
docker compose exec aurora-db psql -U aurora -d aurora_llibres
docker compose exec aurora-db psql -U aurora -d aurora_llibres -c "SELECT count(*) FROM llibres;"
docker compose exec aurora-cache redis-cli INFO keyspace
docker compose exec aurora-api sh                    # shell dins de l'API
docker compose exec -u root aurora-api sh            # com a root, per instal·lar alguna cosa
docker compose exec -T aurora-db pg_dump -U aurora aurora_llibres > copia.sql

-T desactiva l'assignació de pseudo-TTY: obligatori quan redirigeixes la sortida a un fitxer o encadenes canonades, o el bolcat sortirà amb caràcters de control.

# RUN: contenidors nous i efímers
docker compose run --rm aurora-api npm test
docker compose run --rm --no-deps aurora-api node -e "console.log(process.version)"
docker compose run --rm --entrypoint sh aurora-api
docker compose run --rm -e NODE_ENV=test aurora-api npm run test:integracio
docker compose run --rm --service-ports aurora-api    # publica els seus ports
> [email protected] test
> node --test

✔ GET /salut retorna 200 (12.4ms)
✔ GET /llibres retorna 9 títols (31.7ms)
✔ GET /llibres/:id inexistent retorna 404 (4.1ms)
ℹ pass 3

--rm gairebé sempre. Sense ell, cada run deixa un contenidor aturat a la màquina; al cap d'una setmana tens quaranta contenidors aurora-libros-aurora-api-run-a3f9c2 ocupant espai. I --no-deps quan la tasca no necessita la pila: sense ell, un run innocent t'aixeca PostgreSQL i Redis.

  1. Construcció i publicació: build, pull, push

docker compose build                       # construeix els serveis amb build:
docker compose build --no-cache aurora-api # ignora la memòria cau de capes
docker compose build --pull                # refresca la imatge base primer
docker compose build --progress plain      # sortida completa, útil per depurar
docker compose pull                        # descarrega les imatges declarades
docker compose pull --ignore-buildable     # omet els serveis que es construeixen
docker compose push aurora-api             # publica al registre d'imatges
docker compose images
CONTAINER                       REPOSITORY                  TAG      SIZE
aurora-libros-aurora-api-1      auroralibros/aurora-api     1.2.0    142MB
aurora-libros-aurora-cache-1    redis                       7-alpine 41.4MB
aurora-libros-aurora-db-1       postgres                    16-alpine 274MB
aurora-libros-aurora-web-1      nginx                       alpine   48.3MB

El flux típic de CI, que veuràs complet a la lliçó 06-02, cap en tres línies: docker compose build, docker compose push i, al servidor, docker compose pull && docker compose up -d.

  1. Escalat local amb --scale i els seus límits

docker compose up -d --scale aurora-api=3
docker compose ps --format "table {{.Name}}\t{{.Ports}}"
Error response from daemon: driver failed programming external connectivity:
Bind for 0.0.0.0:3000 failed: port is already allocated

Aquí hi ha el primer xoc: un port fix del host no es pot repartir entre tres rèpliques. Només hi ha un 3000 a la màquina. I si el servei tingués container_name, fallaria encara abans, perquè dos contenidors no es poden dir igual.

Obstacle Per què xoca Solució
ports: ["3000:3000"] Un sol port del host Treure el port o fer servir - "3000" (aleatori)
container_name Noms duplicats No fer servir container_name
Volum de dades compartit Dos Postgres sobre els mateixos fitxers No escalar serveis amb estat

Traient la publicació fixa, funciona:

docker compose up -d --scale aurora-api=3
docker compose ps --services --filter status=running | sort | uniq -c
docker compose exec aurora-web getent hosts aurora-api
172.20.0.5   aurora-api
172.20.0.7   aurora-api
172.20.0.8   aurora-api

El DNS intern de Docker retorna les tres adreces per al mateix nom, i el client en tria una. És un repartiment rudimentari —sense comprovació de salut, sense pesos, sense reintents— que serveix per a proves locals, no per a producció. El balanceig real, amb les seves estratègies i la seva gestió de rèpliques caigudes, és la lliçó 06-06.

docker compose up -d --scale aurora-api=1    # tornar a una rèplica

  1. Diagnòstic: config i validació a CI

docker compose config llegeix tots els fitxers implicats, interpola variables, fusiona overrides i mostra la configuració final tal com l'entén Compose. És la teva única manera de veure la veritat.

docker compose config                  # configuració completa resolta
docker compose config --quiet          # només valida: sense sortida, codi 0 o 1
docker compose config --services       # noms dels serveis
docker compose config --volumes        # noms dels volums
docker compose config --images         # imatges que es faran servir
docker compose config --no-interpolate # deixa els ${...} sense resoldre
docker compose config --format json | jq '.services["aurora-api"].deploy'
{ "resources": { "limits": { "memory": "268435456", "cpus": "1.0" } } }

En una canonada de CI, --quiet és la primera comprovació que ha de córrer, abans fins i tot de construir res:

docker compose config --quiet || { echo "compose.yaml no vàlid"; exit 1; }

Dos avisos sobre config: resol les variables, així que la seva sortida pot contenir contrasenyes en clar (no la bolquis mai en un log públic), i mostra el resultat normalitzat, amb les formes curtes convertides en formes llargues. Aquesta normalització és justament el que necessites per entendre què va fer una fusió d'àncores o de fitxers.

  1. Selecció de fitxers i projecte: -f, -p, COMPOSE_FILE

Les opcions globals van abans de la subcomanda:

docker compose -f ~/aurora-libros/compose.yaml ps        # correcte
docker compose ps -f ~/aurora-libros/compose.yaml        # ERROR
Opció Què fa
-f, --file Fitxer que cal fer servir. Repetible, i l'ordre importa
-p, --project-name Nom del projecte (prefix de tots els objectes)
--project-directory Directori base per resoldre rutes relatives
--profile Activa un perfil (lliçó 04-06)
--env-file Fitxer de variables per a la interpolació (lliçó 04-05)
docker compose -f compose.yaml -f compose.prod.yaml -p aurora-prod up -d

Amb diversos -f, Compose fusiona els fitxers en l'ordre donat i l'últim guanya en cas de conflicte. El mecanisme de fusió complet, amb les seves regles per tipus de clau, és la lliçó 04-06.

Un detall que sorprèn: quan fas servir -f, el directori base per a les rutes relatives passa a ser el del primer fitxer. Si els teus fitxers estan repartits, --project-directory et deixa fixar-lo explícitament.

I la variable d'entorn equivalent, útil per no repetir -f cent vegades al dia:

export COMPOSE_FILE=compose.yaml:compose.prod.yaml   # separador ":" a Linux/macOS
export COMPOSE_PROJECT_NAME=aurora-prod
docker compose up -d      # fa servir tots dos fitxers i el nom de projecte

  1. Autocompletat i àlies útils

# Bash: completat de subcomandes, serveis i opcions
docker completion bash | sudo tee /etc/bash_completion.d/docker > /dev/null
# Zsh
docker completion zsh > "${fpath[1]}/_docker"

Amb l'autocompletat actiu, docker compose logs -f aur<TAB> t'ofereix els serveis reals del projecte: s'ha acabat teclejar malament els noms.

alias dc='docker compose'
alias dcu='docker compose up -d'
alias dcl='docker compose logs -f --tail 100'
alias dcp='docker compose ps'
alias dce='docker compose exec'
# Deliberadament NO existeix un àlies amb "down -v"

Aquest últim comentari no és cap broma: la major part de les pèrdues de dades amb Compose vénen d'un àlies curt que algú va escriure amb -v "per netejar ràpid".

Errors Habituals i Consells

Fer servir restart esperant que apliqui canvis del fitxer. No ho fa. Canvi al compose.yamlup -d, sempre.

Confondre stop amb down. stop conserva els contenidors, down els elimina juntament amb la xarxa. Si després d'un down esperaves trobar el contenidor aturat, no hi és.

Teclejar down -v per costum. Esborra els volums amb nom i amb ells les dades. Pensa-t'ho cada vegada.

Oblidar --rm a run. Cada execució deixa un contenidor aturat. Neteja els acumulats amb docker compose rm -f.

Executar exec amb redirecció sense -T. El pseudo-TTY insereix caràcters de control i corromp bolcats i fitxers binaris.

Posar -f després de la subcomanda. Les opcions globals van abans; l'error que retorna la CLI no sempre ho deixa clar.

Consell: quan alguna cosa no es comporta com esperes, l'ordre de diagnòstic és docker compose config (què entén Compose?), docker compose ps (què està corrent?) i docker compose logs (què diu el servei?). En aquest ordre resols gairebé tot.

Exercicis

Exercici 1. Amb la pila aixecada, canvia el límit de memòria d'aurora-cache de 256M a 320M i aplica'l sense que es reiniciïn els altres tres serveis. Demostra amb comandes que només es va recrear la memòria cau i que el límit nou està actiu.

Exercici 2. Escriu un script verificar.sh apte per a CI que: validi el fitxer, aixequi la pila esperant que estigui sana, executi les proves de l'API en un contenidor efímer, imprimeixi l'estat dels quatre serveis i netegi tot, volums inclosos, retornant el codi de sortida de les proves.

Exercici 3. Sense apagar la pila, obtén: (a) el nombre de claus a Redis, (b) els tres primers títols del catàleg ordenats alfabèticament, i (c) un bolcat de la taula llibres al fitxer ~/llibres.sql del host. Explica quina opció és imprescindible en el tercer cas i per què.

Solucions

Solució 1.

docker compose ps --format "{{.Service}}: {{.RunningFor}}"
aurora-api: 22 minutes ago
aurora-cache: 22 minutes ago
aurora-db: 22 minutes ago
aurora-web: 22 minutes ago

Edita el límit al compose.yaml (limits: { memory: 320M, cpus: "0.5", pids: 100 }) i aplica:

docker compose up -d
 ✔ Container aurora-libros-aurora-db-1     Running
 ✔ Container aurora-libros-aurora-cache-1  Recreated
 ✔ Container aurora-libros-aurora-api-1    Running
 ✔ Container aurora-libros-aurora-web-1    Running

Aquí hi ha la reconciliació: Recreated només a la memòria cau, Running als altres tres perquè el seu config-hash no va canviar. Verificació:

docker compose ps --format "{{.Service}}: {{.RunningFor}}"
docker inspect aurora-libros-aurora-cache-1 --format '{{.HostConfig.Memory}}'
aurora-cache: 4 seconds ago
335544320

La memòria cau porta quatre segons i els altres continuen amb els seus vint-i-dos minuts. I 335544320 bytes són exactament 320 MiB. Compte: recrear un contenidor no és reiniciar-lo, és destruir-lo i crear-ne un altre; les dades que no siguin en un volum es perden. A Redis, això significa una memòria cau buida, que aquí és inofensiu perquè /llibres la repobla des de la base de dades.

Solució 2.

#!/usr/bin/env bash
# verificar.sh — pipeline local d'Aurora Libros
set -uo pipefail
cd "$(dirname "$0")"

docker compose config --quiet || { echo "compose.yaml no vàlid"; exit 1; }

docker compose up -d --build --wait --wait-timeout 90 || {
  echo "La pila no ha arribat a un estat sa"
  docker compose logs --tail 40
  docker compose down -v
  exit 1
}

docker compose run --rm --no-deps aurora-api npm test
codi=$?

docker compose ps --format "table {{.Service}}\t{{.Status}}"
docker compose down -v --remove-orphans
exit $codi

Quatre decisions importants: --quiet valida abans de gastar temps construint; --wait amb --wait-timeout garanteix que les proves no arrenquin contra una base de dades que encara no respon i falla amb codi diferent de zero si no ho aconsegueix; --rm --no-deps a les proves evita deixar brossa i no torna a aixecar dependències que ja estan sanes; i down -v aquí és correcte i desitjable, perquè és un entorn d'un sol ús. Fixa't que es desa $? abans de les comandes de neteja: si no, el codi de sortida que retornaria l'script seria el de down, i el pipeline passaria sempre en verd.

Solució 3.

# (a) claus a la memòria cau
docker compose exec aurora-cache redis-cli DBSIZE
# (b) tres primers títols
docker compose exec aurora-db psql -U aurora -d aurora_llibres \
  -c "SELECT titol FROM llibres ORDER BY titol LIMIT 3;"
# (c) bolcat al host
docker compose exec -T aurora-db pg_dump -U aurora -t llibres aurora_llibres > ~/llibres.sql
(integer) 1
                  titol
------------------------------------------
 Cien años de soledad
 El Aleph
 El jardín de senderos que se bifurcan
(3 rows)

En el tercer cas -T és imprescindible. Sense ell, Compose assigna un pseudo-TTY a la comanda i el flux de sortida deixa de ser binari net: es tradueixen els salts de línia i s'hi colen seqüències de control, amb la qual cosa el .sql resultant pot no tornar-se a importar. La regla és senzilla: si la sortida no la llegiràs tu amb els ulls, -T.

Conclusió

Ja manegues la CLI completa. Saps que up no és "arrencar" sinó "reconciliar": compara el resum de configuració de cada servei amb el declarat i recrea només allò que va canviar, la qual cosa et permet ajustar un límit i tocar un únic contenidor. En coneixes les opcions decisives —-d, --build, --pull, --force-recreate, --no-deps, --remove-orphans i sobretot --wait, que espera que les sondes de salut passin i falla si no ho fan—. I tens clar què esborra cada variant de down, amb -v marcat en vermell.

Distingeixes start/stop/restart/pause d'up/down, amb el parany que restart no rellegeix el fitxer. Observes amb ps (inclosos --services i --format json), logs, top, stats, events i port. I tens interioritzada la diferència clau: exec entra en allò que ja corre, run crea un contenidor nou, amb --rm per no acumular brossa, --no-deps per no aixecar mitja pila i -T quan redirigeixes la sortida. Construeixes i publiques amb build/pull/push, escales localment amb --scale sabent que xoca amb els ports fixos i amb container_name, valides amb config --quiet a CI i controles quin fitxer i quin projecte fas servir amb -f, -p i COMPOSE_FILE.

A la lliçó següent, Aplicacions Multi-Contenidor, tot això es posa al servei del muntatge definitiu: l'arquitectura d'Aurora Libros amb dues xarxes segmentades —una de frontal i una de posterior marcada com a internal—, el DNS de Compose i la taula de qui parla amb qui i per quin port, l'ordre d'arrencada resolt amb depends_on i condition: service_healthy fent servir pg_isready i redis-cli ping, un servei de migració amb service_completed_successfully, i el patró de reintent amb backoff que cal encara que tinguis tot l'anterior. Al final, els quinze passos d'onboarding es quedaran en dos.

Docker: De Principiant a Avançat

Mòdul 1: Introducció a Docker

Mòdul 2: Treballant amb Imatges Docker

Mòdul 3: Contenidors Docker

Mòdul 4: Docker Compose

Mòdul 5: Conceptes Avançats de Docker

Mòdul 6: Docker en Producció

Mòdul 7: Ecosistema i Eines de Docker

© Copyright 2026. Tots els drets reservats