01The Problem With Always-Running Services
02How Profiles Work
1services: 2 # No profile — always starts3 app: 4 image: myapp:latest5 ports: 6 - "8080:8080"78 # No profile — always starts9 db: 10 image: postgres:16-alpine11 volumes: 12 - pgdata:/var/lib/postgresql/data1314 # Only starts with --profile debug15 pgadmin: 16 image: dpage/pgadmin4:latest17 profiles: [debug]18 ports: 19 - "5050:80"20 environment: 21 - PGADMIN_DEFAULT_EMAIL=admin@local.dev22 - PGADMIN_DEFAULT_PASSWORD=admin2324 # Only starts with --profile monitoring25 prometheus: 26 image: prom/prometheus:latest27 profiles: [monitoring]28 ports: 29 - "9090:9090"3031 # Multiple profiles — starts with either32 grafana: 33 image: grafana/grafana:latest34 profiles: [monitoring, debug]35 ports: 36 - "3000:3000"A service can belong to multiple profiles. In the example above, Grafana starts if you activate either the monitoring or debug profile. Use docker compose --profile debug --profile monitoring up to activate multiple profiles at once.
03Practical Profile Patterns
1services: 2 app: 3 image: myapp:latest4 ports: 5 - "8080:8080"6 depends_on: 7 db: 8 condition: service_healthy910 db: 11 image: postgres:16-alpine12 healthcheck: 13 test: ["CMD", "pg_isready"]14 interval: 5s15 timeout: 3s16 retries: 51718 # --- Debug Profile ---19 pgadmin: 20 profiles: [debug]21 image: dpage/pgadmin4:latest22 ports: 23 - "5050:80"2425 mailpit: 26 profiles: [debug]27 image: axllent/mailpit:latest28 ports: 29 - "8025:8025" # Web UI30 - "1025:1025" # SMTP3132 # --- Monitoring Profile ---33 prometheus: 34 profiles: [monitoring]35 image: prom/prometheus:latest3637 node-exporter: 38 profiles: [monitoring]39 image: prom/node-exporter:latest4041 # --- Seed Profile (run once, then exit) ---42 seed: 43 profiles: [seed]44 image: myapp:latest45 command: ["npm", "run", "seed"]46 depends_on: 47 db: 48 condition: service_healthy04Using COMPOSE_PROFILES in .env
1# .env file — these profiles activate automatically2# Comma-separated list, no spaces3COMPOSE_PROFILES=debug,monitoring45# Now just run:6# docker compose up -d7# Equivalent to: docker compose --profile debug --profile monitoring up -dServices without any profile always start, regardless of which profiles are active. This is intentional — your core services (app, database, cache) should have no profile so they always run. Only add profiles to genuinely optional services. A common mistake is adding profiles to everything, which means docker compose up with no profiles starts nothing.