How To Use Docker Compose
Learn how to manage multi-container applications with Docker Compose using compose.yaml, service profiles, and essential CLI commands.

Docker Compose is a multi-container orchestration tool for a single host. It uses the Docker engine to manage and orchestrate the Docker containers. It simplifies your local development environment by allowing you to create complex and interconnected containers declaratively. Docker Compose also automates the test suite in the CI/CD process by providing a temporary and isolated environment for testing.
This article explains how to use Docker Compose to create and manage containers. It covers the basics of the docker compose command and how to create the compose.yaml manifest. It also explores the Docker Compose profile for running containers depending on your environment.
The Short Answer
Below are some basic Docker Compose commands that operate on a compose.yaml or docker-compose.yaml file.
# Create/build and start all services in the background.
$ docker compose up -d
# Stop and remove all service containers, networks, and volumes.
$ docker compose down
# Stop all services.
$ docker compose stop
# Start and run stopped services.
$ docker compose start
# View the status of running services.
$ docker compose ps
# Check the logs of all services.
$ docker compose logs
Create a Docker Compose File
This section covers creating a sample Docker Compose file compose.yaml for a sample flask application.
- Download the sample application.
console
$ git clone https://github.com/ndlrfz/flask-redis-postgresql - Navigate to the
flask-redis-postgresqlproject directory.console$ cd flask-redis-postgresql
- The directory contains a file
compose.yamlwith the following contents.console$ cat compose.ymlYou’ll see the following Docker Compose manifest:
yamlname: myproject services: redis: image: "redis:alpine" restart: always ports: - 6379:6379 volumes: - redis:/data postgresql: image: "postgres:16-alpine" ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_USER: ${DATABASE_USER} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_DB: ${DATABASE_NAME} flask: build: . restart: always env_file: - ".env" depends_on: - redis - postgresql ports: - "8000:5000" volumes: - ./:/code volumes: redis: driver: "local" pgdata: driver: "local"
In this file,
name: Sets the project name tomyproject.services: Defines the individual containers that make up your application.- Define the
redisservice with these details:image: Creates the service from theredis:alpineimage.restart: always: Sets a policy to always restart this container if it stops.ports: Maps port6379on the host to port6379in the container.volumes: Mounts the named volumeredisto the/datadirectory inside the container for persistent data.
- Define the
postgresqlservice with the following:image: Creates the service from thepostgres:16-alpineimage.ports: Maps port5432on the host to port5432in the container.volumes: Mounts the named volumepgdatato the/var/lib/postgresql/datadirectory for persistent data.environment: Sets environment variables inside the container. The values for${DATABASE_USER},${DATABASE_PASSWORD}, and${DATABASE_NAME}are substituted from your shell’s environment (often loaded from an.envfile).
- Define the application service
flaskwith the following:build: .: Builds a Docker image in the current directory from theDockerfile.restart: always: Sets a policy to always restart this container if it stops.env_file: Loads environment variables for the service directly from a.envfile.depends_on: Controls the startup order. Theflaskservice will start only after theredisandpostgresqlservices have been started.ports: Maps port8000on the host to port5000inside the container.volumes: Mounts the current project directory on the host to the/codedirectory inside the container.
volumes: Defines two named volumes,redisandpgdata, using thelocaldriver. These are managed by Docker and stored on the host machine.
- Create a file
.env.console$ nano .envAdd the following environment variables for PostgreSQL credentials. You can replace the following details with your credentials.
iniDATABASE_USER=app_user DATABASE_PASSWORD=app_password DATABASE_NAME=appdb DATABASE_HOST=postgresql DATABASE_PORT=5432
Save and exit the file.
- Build and start your services.
console
$ docker compose up -dThe option
-dallows you to run containers in the background. - Run the command below to check the running containers or services.
console
$ docker compose psOutput:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS myproject_flask-1 myproject_flask "flask run --debug" flask About a minute ago Up About a minute 0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp myproject_postgresql-1 postgres:16-alpine "docker-entrypoint.s…" postgresql About a minute ago Up About a minute 0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcp myproject_redis-1 redis:alpine "docker-entrypoint.s…" redis About a minute ago Up 32 seconds 0.0.0.0:6379->6379/tcp, [::]:6379->6379/tcpOutput explanation:
flaskservice is running on port8000on the host machine, which is based on the Docker imagemyproject_flask.postgresqlservice running on port5432based on the Docker imagepostgres:16-alpine.redisservice is running on port6379based on the Docker imageredis:alpine.
- Check the list of images that are used by your services.
console
$ docker compose imagesOutput:
CONTAINER REPOSITORY TAG IMAGE ID SIZE myproject_flask-1 myproject_flask latest 1c3d151b6276 254MB myproject_postgresql-1 postgres 16-alpine b56133b65cd3 275MB myproject_redis-1 redis alpine d470ca4bc10c 60.5MB - Open a web browser and visit
http://<SERVER-IP>:8000/to verify that your application is running.
Manage Docker Compose Stack
The following table describes the common docker compose commands for managing your application stack.
| Command | Description | Example |
|---|---|---|
build |
Build or rebuild containers | docker compose build |
up |
Create and start containers | docker compose up -d |
down |
Stop and remove containers | docker compose down |
start |
Start existing containers | docker compose start app |
stop |
Stop containers | docker compose stop app |
kill |
Force stop containers | docker compose kill app |
ps |
Check running containers | docker compose ps |
logs |
Check logs for containers | docker compose logs app |
exec |
Execute command in the container | docker compose exec app bash |
images |
Listing images used by containers | docker compose images |
rm |
Remove stopped containers | docker compose rm |
Use Docker Compose Profiles
Profiles allow selective applications or services to run based on the environment or use cases, such as development and production.
- Edit the
compose.ymlfile.console$ nano compose.yml - Add the following configuration under the
flaskservice.yamladminer: image: adminer restart: always depends_on: [postgresql] profiles: [dev] ports: - 8080:8080 redis-insight: image: redis/redisinsight:latest restart: always profiles: [dev] depends_on: [redis] ports: - "8001:8001"
Save and close the file.
In this configuration:
adminer: Defines a service namedadminerthat uses theadminerDocker image.restart: always: Sets a policy to always restart the container if it stops.depends_on: Ensures theadminerservice starts only after thepostgresqlservice starts.profiles: Assigns thedevprofile to the service.ports: Maps port8080on the host to port8080in the container.
redis-insight: Defines a service namedredis-insightthat uses theredis/redisinsight:latestDocker image.restart: always: Sets a policy to always restart the container if it stops.depends_on: Ensures theredis-insightservice starts only after theredisservice starts.profiles: Assigns thedevprofile to the service.ports: Maps port8001on the host to port8001in the container.
- Start the
adminerservice.console$ docker compose --profile dev up -dUse the
--profile devoption to activate and start services assigned to thedevprofile. This tells Docker Compose to include theadminerservice, which it ignores by default. - Check the running container with the following command.
console
$ docker compose psYou’ll see the
adminerservice is running.NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS myproject-adminer-1 adminer "entrypoint.sh docke…" adminer 2 seconds ago Up 1 second 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp myproject-flask-1 myproject-flask "flask run --debug" flask 2 seconds ago Up 1 second 0.0.0.0:8000->5000/tcp, [::]:8000->5000/tcp myproject-postgresql-1 postgres:16-alpine "docker-entrypoint.s…" postgresql 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcp myproject-redis-1 redis:alpine "docker-entrypoint.s…" redis 3 seconds ago Up 2 seconds 0.0.0.0:6379->6379/tcp, [::]:6379->6379/tcp myproject-redis-insight-1 redis/redisinsight:latest "./docker-entry.sh n…" redis-insight 3 seconds ago Up 1 second 5540/tcp, 0.0.0.0:8001->8001/tcp, [::]:8001->8001/tcp - To stop a service, for example
adminer, useconsole$ docker compose stop adminer - Remove the stopped
adminerservice.console$ docker compose rm adminer - Destroy all services (including the
devprofile).console$ docker compose --profile dev down
Conclusion
In this article, you’ve explored how to use Docker Compose to manage container stacks. You have learned how to start, stop, restart, and destroy containers using the docker compose command, how to create the compose.yaml file for your application stack, and the basic usage of Docker Compose Profiles for selectively running containers depending on your environment.