Docker Compose Examples

Organize Docker compose.yaml examples here.

Application

services:  
app:
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
- xxx:xxx
environment:
- xxx=xxx
db:
image: 'xxx'
...
cache:
image: 'xxx'
...

Database

Postgres

Postgres DockerHub

Run Postgres with Docker Compose

compose.yaml

services:
postgres1:
image: 'postgres:17-alpine'
container_name: 'postgres1'
ports:
- '${POSTGRES_PORT}:5432'
environment:
- TZ=Asia/Shanghai
# Note: Password can only be set on first startup. You can delete the volume to set a new password.
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
volumes:
- 'postgres_data:/var/lib/postgresql/data'
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 5s
timeout: 5s
retries: 5

volumes:
postgres_data:
driver: local

Verify

mkdir my-app && cd my-app
vim compose.yaml
# paste the compose.yaml. Save and exit.

# set temporary environment variables
export POSTGRES_PORT=15432 && export POSTGRES_PASSWORD=STRONG_PASSWORD
# start docker compose
docker compose up -d
docker compose logs -f postgres1
# verify
docker compose exec -it postgres1 psql -V
docker compose exec -it postgres1 psql -U postgres

Run Postgres with docker run command

docker run -d \
--name postgres1 \
-p 5432:5432 \
-e POSTGRES_PASSWORD=STRONG_PASSWORD \
-e TZ=Asia/Shanghai \
-v postgres_data:/var/lib/postgresql/data \
--restart always \
postgres:17-alpine

MySQL

MySQL DockerHub

MySQL server DockerHub

MySQL server docker environment variables

Run MySQL with Docker Compose

compose.yaml

services:
mysql1:
image: 'mysql/mysql-server:8.0'
container_name: 'mysql1'
ports:
- '${MYSQL_PORT}:3306'
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_HOST=%
- 'MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}'
volumes:
- 'mysql_data:/var/lib/mysql'
restart: on-failure
healthcheck:
test: "mysql -uroot -p$$MYSQL_ROOT_PASSWORD -e 'show databases'"
interval: 5s
timeout: 5s
retries: 5

volumes:
mysql_data:
driver: local
  • image: mysql:8 or mysql/mysql-server:8.0

Verify

mkdir my-app && cd my-app
vim compose.yaml
# paste the compose.yaml. Save and exit.

# set temporary environment variables
export MYSQL_PORT=13306 && export MYSQL_PASSWORD=STRONG_PASSWORD
# start docker compose
docker compose up -d
docker compose logs -f mysql1
# verify
docker compose exec -it mysql1 mysql -V
docker compose exec -it mysql1 mysql -uroot -p

Run MySQL with docker run command

docker run --name=mysql1 -d -p 3306:3306 \
-e TZ=Asia/Shanghai -e MYSQL_ROOT_HOST='%' -e MYSQL_ROOT_PASSWORD=STRONG_PASSWORD \
--restart on-failure \
-v mysql_data:/var/lib/mysql \
mysql/mysql-server:8.0

Full-Text Search Engine

Elasticsearch + Kibana + Logstash (ELK) v9.x

Install Elasticsearch with Docker

Install Kibana with Docker

Running Logstash on Docker

ELK without password and enrollment token for local development

1. compose.yaml

services:
elasticsearch1:
image: "elasticsearch:9.0.4"
ports:
- "${ELASTICSEARCH_PORT}:9200"
environment:
- discovery.type=single-node
- "xpack.security.enabled=false"
mem_limit: 1GB
networks:
- elastic
healthcheck:
test: "curl --fail -X GET http://localhost:9200/_cat/health || exit 1"
interval: 5s
timeout: 5s
retries: 10
kibana1:
image: "kibana:9.0.4"
depends_on:
elasticsearch1:
condition: service_healthy
ports:
- "${KIBANA_PORT}:5601"
environment:
- "ELASTICSEARCH_HOSTS=http://elasticsearch1:9200"
networks:
- elastic
healthcheck:
# Or test: "curl -f http://localhost:5601/status || exit 1"
test: "curl http://localhost:5601/api/status | grep 'All services and plugins are available'"
interval: 5s
timeout: 5s
retries: 10
logstash1:
image: "logstash:9.0.4"
depends_on:
elasticsearch1:
condition: service_healthy
networks:
- elastic

networks:
elastic:
driver: bridge
  • xpack.security.enabled=false: Disabling security.

2. Running ELK

docker compose down
docker compose up -d

3. Visiting Kibana Web Page http://localhost:5601/

ELK with enrollment token and password

1. compose.yaml

services:
elasticsearch1:
image: "elasticsearch:9.0.4"
ports:
- "${ELASTICSEARCH_PORT}:9200"
environment:
- discovery.type=single-node
- "ELASTIC_PASSWORD=${ELASTIC_PASSWORD}"
mem_limit: 1GB
networks:
- elastic
healthcheck:
test: "curl --cacert /usr/share/elasticsearch/config/certs/http_ca.crt -u elastic:${ELASTIC_PASSWORD} --fail -X GET https://localhost:9200/_cat/health || exit 1"
interval: 5s
timeout: 5s
retries: 10
kibana1:
image: "kibana:9.0.4"
depends_on:
elasticsearch1:
condition: service_healthy
ports:
- "${KIBANA_PORT}:5601"
networks:
- elastic
healthcheck:
test: "curl -f http://localhost:5601/status || exit 1"
interval: 5s
timeout: 5s
retries: 10
logstash1:
image: "logstash:9.0.4"
depends_on:
elasticsearch1:
condition: service_healthy
networks:
- elastic
volumes:
- ./docker/logstash/config:/usr/share/logstash/config/


networks:
elastic:
driver: bridge

2. Running ELK

docker compose down
docker compose up -d

3. Visiting Kibana Web Page http://localhost:5601/

4. Get enrollment token

# Get enrollment token
$ docker compose exec -it elasticsearch1 /usr/share/elasticsearch/bin/elasticsearch-create-enrollment-token -s kibana

5. Get verification code

# Get verification code by command
$ docker compose exec kibana1 bin/kibana-verification-code
# Or get verification code by viewing Kibana logs
$ docker compose logs kibana1

6. Login Kibana

  • Username: elastic
  • Password: {your-passowrd}

SSL

Set up HTTPS

Create an encrypted Elasticsearch keystore

elasticsearch-keystore

Elasticsearch + Kibana + Logstash (ELK) v7.x

Install Elasticsearch with Docker

Install Kibana with Docker

Running Logstash on Docker

ELK without password for local development

services:
elasticsearch1:
image: "elasticsearch:7.17.28"
ports:
- "9200:9200"
- "9300:9300"
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
networks:
- elastic
volumes:
- es_data:/usr/share/elasticsearch/data
healthcheck:
test: "curl --fail -X GET http://localhost:9200/_cat/health || exit 1"
interval: 5s
timeout: 5s
retries: 10
kibana1:
image: "kibana:7.17.28"
depends_on:
elasticsearch1:
condition: service_healthy
ports:
- "${KIBANA_PORT}:5601"
environment:
- "ELASTICSEARCH_HOSTS=http://elasticsearch1:9200"
networks:
- elastic
healthcheck:
# Or test: "curl -f http://localhost:5601/status || exit 1"
test: "curl http://localhost:5601/api/status | grep 'Looking good'"
interval: 5s
timeout: 5s
retries: 10
logstash1:
image: "logstash:7.17.28"
depends_on:
elasticsearch1:
condition: service_healthy
networks:
- elastic

volumes:
es_data:
driver: local

networks:
elastic:
driver: bridge

Cache

Redis

Redis DockerHub

Run Redis with Docker Compose

compose.yaml

services: 
redis1:
image: 'redis:7-alpine'
container_name: 'redis1'
ports:
- '${REDIS_PORT}:6379'
volumes:
- 'redis_data:/data'
command: 'redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD}'
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep PONG"]
interval: 5s
timeout: 5s
retries: 5

volumes:
redis_data:
driver: local

Verify

mkdir my-app && cd my-app
vim compose.yaml
# paste the compose.yaml. Save and exit.

# set temporary environment variables
export REDIS_PORT=16379 && export REDIS_PASSWORD=STRONG_PASSWORD
# start docker compose
docker compose up -d
docker compose logs -f redis1
# verify
docker compose exec -it redis1 redis-server -v
docker compose exec -it redis1 redis-cli

Run Redis with docker run command

docker run --name redis1 -d -p 16379:6379 \
-v redis_data:/data \
redis \
redis-server \
--save 60 1 \
--loglevel warning \
-requirepass STRONG_PASSWORD

Web Server

Nginx

Nginx DockerHub

Run Nginx with Docker Compose

services:
nginx1:
image: 'nginx:stable-alpine'
container_name: 'nginx1'
ports:
- '${NGINX_PORT}:${NGINX_PORT}'
volumes:
# The nginx.conf file mapping is optional. You can just put your domain config to /etc/nginx/conf.d/mydomain.conf
- ./nginx.conf:/etc/nginx/nginx.conf:ro
#- ./conf.d/:/etc/nginx/conf.d/:ro
#- ./dist:/usr/share/nginx/html:ro
restart: unless-stopped
healthcheck:
test: "curl --fail http://localhost/ || exit 1"
interval: 5s
timeout: 5s
retries: 5

Verify

mkdir my-app && cd my-app
vim compose.yaml
# paste the compose.yaml. Save and exit.

# get nginx.conf
docker run --rm --entrypoint=cat nginx:stable-alpine /etc/nginx/nginx.conf > ./nginx.conf
# set temporary environment variables
export NGINX_PORT=80
# start docker compose
docker compose up -d
docker compose logs -f nginx1
# verify
docker compose exec -it nginx1 nginx -v
docker compose exec -it nginx1 nginx -t
curl http://localhost:80

Message Queue

RabbitMQ

RabbitMQ DockerHub

services:
rabbitmq1:
image: 'rabbitmq:4-management'
container_name: 'rabbitmq1'
ports:
- '${RABBIT_PORT}:5672'
- '${RABBIT_MANAGEMENT_CONSOLE_PORT}:15672'
# The default username and password of RabbitMQ are guest / guest
environment:
- TZ=Asia/Shanghai
- RABBITMQ_DEFAULT_USER=${RABBIT_USER}
- 'RABBITMQ_DEFAULT_PASS=${RABBIT_PASSWORD}'
restart: unless-stopped
healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 5s
timeout: 5s
retries: 5

Open the RabbitMQ management console by visiting http://localhost:15672

Run RabbitMQ with the docker run command

# latest RabbitMQ 4.x
docker run -d --name rabbitmq1 -p 5672:5672 -p 15672:15672 rabbitmq:4-management

Service Discovery

Apache ZooKeeper

Apache ZooKeeper DockerHub

services:
zookeeper1:
image: 'zookeeper:3.9'
restart: unless-stopped
hostname: zoo1
ports:
- '${ZOOKEEPER_PORT}:2181'

Run ZooKeeper with the docker run command

$ docker run --name some-zookeeper -p 2181:2181 --restart unless-stopped -d zookeeper

Vault

HashiCorp Vault

HashiCorp Vault DockerHub

services:  
vault1:
image: 'hashicorp/vault:1.20'
ports:
- ${VAULT_PORT}:8200
cap_add:
- IPC_LOCK
environment:
- 'VAULT_DEV_ROOT_TOKEN_ID=${VAULT_DEV_ROOT_TOKEN_ID}'

Run RabbitMQ with the docker run command

docker run -d --name=dev-vault -p 8200:8200 -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' --cap-add=IPC_LOCK hashicorp/vault:1.20
  • --cap-add=IPC_LOCK: This is required in order for Vault to lock memory, which prevents it from being swapped to disk. This is highly recommended.

Authentication, Authorization, and User Management

Keycloak

Keycloak on Docker guide

Running Keycloak in a container

services:   
keycloak1:
image: 'quay.io/keycloak/keycloak:26.3'
ports:
- ${KEYCLOAK_PORT}:8080
environment:
- TZ=Asia/Shanghai
- 'KC_BOOTSTRAP_ADMIN_USERNAME=${KC_BOOTSTRAP_ADMIN_USERNAME}'
- 'KC_BOOTSTRAP_ADMIN_PASSWORD=${KC_BOOTSTRAP_ADMIN_PASSWORD}'
command: start-dev
  • command:
    • start-dev: Start the server in development mode for local development or testing.
    • start <OPTIONS>: Start the server in production mode.

Run Keycloak with the docker run command

docker run -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:26.3 start-dev

Keycloak with Postgres

click to expand!
services:
keycloak1:
image: 'quay.io/keycloak/keycloak:26.3'
depends_on:
postgres1:
condition: service_healthy
ports:
- ${KEYCLOAK_PORT}:8080
environment:
- TZ=Asia/Shanghai
- 'KC_BOOTSTRAP_ADMIN_USERNAME=${KC_BOOTSTRAP_ADMIN_USERNAME}'
- 'KC_BOOTSTRAP_ADMIN_PASSWORD=${KC_BOOTSTRAP_ADMIN_PASSWORD}'
- 'KC_DB=postgres'
- 'KC_DB_URL=jdbc:postgresql://postgres1:${POSTGRES_PORT}/keycloak'
- 'KC_DB_USERNAME=postgres'
- 'KC_DB_PASSWORD=${POSTGRES_PASSWORD}'
networks:
- keycloak
command: start-dev
postgres1:
image: 'postgres:17-alpine'
ports:
- '${POSTGRES_PORT}:5432'
environment:
- TZ=Asia/Shanghai
# Note: Password can only be set on first startup. You can delete the volume to set a new password.
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
- POSTGRES_DB=keycloak
volumes:
- 'postgres_data_for_keycloak:/var/lib/postgresql/data'
restart: unless-stopped
networks:
- keycloak
healthcheck:
test: [ "CMD-SHELL", "pg_isready" ]
interval: 5s
timeout: 5s
retries: 5

volumes:
postgres_data_for_keycloak:
driver: local

networks:
keycloak:
driver: bridge

Logto

logto GitHub

Reference compose.yaml

services:   
logto:
depends_on:
postgres:
condition: service_healthy
image: 'svhd/logto:latest'
entrypoint: [ "sh", "-c", "npm run cli db seed -- --swe && npm start" ]
ports:
- 3001:3001
- 3002:3002
environment:
- TRUST_PROXY_HEADER=1
- DB_URL=postgres://postgres:p0stgr3s@postgres:5432/logto
# Mandatory for GitPod to map host env to the container, thus GitPod can dynamically configure the public URL of Logto;
# Or, you can leverage it for local testing. - ENDPOINT
- ADMIN_ENDPOINT
postgres:
image: postgres:17-alpine
container_name: postgres-for-logto
user: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: p0stgr3s
healthcheck:
test: [ "CMD-SHELL", "pg_isready" ]
interval: 5s
timeout: 5s
retries: 5

Appendixes

Using environment variables in healthcheck command. For example,

healthcheck:
test: "echo ${MY_VARIABLE}"

or

healthcheck:
test: "echo $$MY_VARIABLE"