This Docker Compose setup guide explains how to define, start, monitor, update, and troubleshoot a multi-container application. You will create a practical Compose file, configure environment variables, persist data with volumes, connect services through networks, inspect logs, manage secrets safely, and establish a recurring review routine so your deployment remains understandable and maintainable.
Overview
Docker Compose lets you describe related containers in one YAML configuration file and operate them as an application stack. A typical stack may include an application server, a database, a cache, and a reverse proxy. Instead of remembering separate container commands, you can define the desired services once and use the Compose command to start or stop the group.
This walkthrough assumes Docker and the Compose command are already installed on the machine where you will run the project. Commands shown with docker compose use the current Compose command style. If your installation uses the older standalone command, the equivalent may be written as docker-compose.
Create a dedicated project directory first:
mkdir my-stack
cd my-stack
Keep the Compose file, application configuration, deployment notes, and any non-secret example environment file together. A predictable directory makes routine checks, backups, and team handoffs easier.
A minimal Compose file
Create a file named compose.yaml:
services:
app:
image: your-app-image:stable
ports:
- "8080:8080"
environment:
APP_MODE: production
DATABASE_URL: ${DATABASE_URL}
depends_on:
- db
networks:
- backend
db:
image: your-database-image:stable
environment:
DB_NAME: appdb
DB_USER: appuser
DB_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/database
networks:
- backend
volumes:
db_data:
networks:
backend:
Replace image names, ports, environment variables, and database paths with values required by your application. The example demonstrates the main building blocks: services, published ports, environment variables, a named volume, and a private application network.
What to track
Service definitions and image versions
Record which services exist and what each one does. For every service, track its image reference, exposed or published ports, startup dependencies, mounted paths, health checks, and restart behavior if configured. Prefer deliberate image tags or digests over an undefined moving target when repeatable deployments matter.
Before changing an image, note the currently deployed reference. This gives you a clear comparison point and makes rollback planning more practical. Read the image documentation for required configuration changes rather than assuming that a newer image accepts the same variables or directory layout.
Environment variables
Separate ordinary configuration from credentials. A local .env file can be convenient for variable substitution, but it should be protected from accidental commits and should not be treated as a complete secret-management system. Keep a safe template such as .env.example containing variable names without real passwords or tokens.
At each review, check for missing variables, obsolete names, unexpected defaults, and differences between development and production. Confirm that values such as database hostnames use the Compose service name when containers communicate on the shared network. For example, an application normally reaches the service named db at hostname db, not at localhost.
Volumes, networks, and exposure
Track which data is persistent and where it is stored. Removing a container does not necessarily remove a named volume, but deleting a volume can destroy its data. Treat volume cleanup commands as potentially destructive and confirm backups before using them.
Review published ports with particular care. A port mapping such as 127.0.0.1:8080:8080 limits access to the local host, while 8080:8080 may publish the service on all available interfaces, depending on the host configuration. Publish only ports that need to be reached from outside the Compose network. Services that communicate internally usually do not need a host port at all.
Operational signals
Track container state, restart counts, recent errors, disk usage, database growth, and response behavior from the application. A stack can show running containers while the application is still unhealthy. Where supported, add health checks and verify the actual dependency, not just whether a process exists.
Cadence and checkpoints
Use a short checklist whenever the stack changes and a broader review on a monthly or quarterly cadence. The right interval depends on how often the application is deployed and how important its data is.
Before the first start
- Confirm that the image names and required variables are present.
- Review every host port and remove mappings that are not required.
- Confirm volume paths and identify which data needs backup.
- Check the Compose configuration before creating containers:
docker compose config
This command helps reveal YAML or interpolation problems. Do not paste its output into a public issue if it renders sensitive values.
Starting and stopping the stack
Start the services in the background with:
docker compose up -d
List the project containers and their current state:
docker compose ps
Stop containers without removing the project definition with:
docker compose stop
When you need to stop and remove the containers and their Compose-created network, use:
docker compose down
Use docker compose down -v only when you intentionally want to remove the associated named volumes. Treat it as a data-affecting operation.
Routine update checkpoint
For an image update, record the current state, review the image's configuration notes, pull the new image, and recreate the affected services:
docker compose pull
docker compose up -d
Afterward, check service status, logs, application behavior, and data access. If only one service is changing, limit the operation when appropriate, for example docker compose up -d app. Do not assume that a successful recreation proves that an application migration completed correctly.
How to interpret changes
Logs are most useful when reviewed by service and time. Follow all project logs with:
docker compose logs -f
Limit the output to one service when diagnosing a specific failure:
docker compose logs --tail=100 app
docker compose logs --tail=100 db
A container that exits immediately often indicates an invalid command, missing variable, incompatible configuration, or failed application startup. A container that repeatedly restarts may be failing its health check or returning a nonzero exit code. A connection error between services commonly points to the wrong hostname, port, credentials, network, or startup timing.
If the application cannot write files, compare the container path with the mounted volume path and check permissions inside the container. If data disappears after recreation, verify whether the application wrote to a temporary container path instead of the declared volume. If a port is unavailable, inspect for another process or container already using it and confirm the host-side port in the Compose file.
For a more methodical process, isolate one variable at a time: validate the rendered configuration, inspect service status, read the earliest relevant log error, test network reachability, and then recreate only the affected service. The general troubleshooting approach in the software troubleshooting guide can help organize this process. For remote hosts, the SSH manual covers the connection basics needed to inspect a deployment safely.
Handling secrets
Do not place production passwords, private keys, or API tokens directly in a file that is committed to source control. Use the secret mechanism supported by your deployment environment, restrict file permissions for local secret files, and rotate credentials if they are exposed. Be aware that environment variables can appear in process inspection, diagnostic output, or rendered configuration, so limit access to the host and logs.
When to revisit
Revisit this Docker Compose setup guide monthly or quarterly, and immediately after any image, operating-system, application, database, network, or credential change. A recurring review should answer five practical questions:
- Are all running services still required, and are their images intentionally pinned?
- Do the environment variables and secret references match the current application?
- Are volumes backed up, growing normally, and mounted at the paths the application expects?
- Are published ports and network connections limited to what the stack needs?
- Can you explain recent restarts, failed health checks, log errors, and image changes?
Keep a short change record with the date, change made, image references before and after, migration status, validation performed, and rollback decision. This turns a one-time setup into a maintainable operating routine. If the stack is newly deployed, schedule the first review sooner so you can catch incorrect assumptions about storage, resource use, or service dependencies.
For the next review, run docker compose config, docker compose ps, and targeted log checks, then compare the results with your previous notes. Make one controlled change at a time, verify the application from the user-facing side, and update the Compose file and documentation together. That simple habit keeps the configuration, the running containers, and the team’s understanding aligned.