At some point docker-compose file gets complicated and we need to version control it. the typical directory structure looks like this:
todo
├── .env
├── docker-compose.yml
├── todo-api/
├── todo-worker/
└── todo-database/
and our docker-compose.yml
file is like this:
# todo/docker-compose
version: '3.1'
services:
todo-database:
image: postgres:11.3
volumes:
- ./todo-database:/docker-entrypoint-initdb.d/
environment:
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
todo-api:
build: ./todo-api
environment:
APP_KEY: $APP_KEY
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@todo-database:5432/postgres
todo-worker:
build: ./todo-worker
environment:
DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@todo-database:5432/postgres
and here is our .env
file
# todo/.env
POSTGRES_PASSWORD=secret-1
APP_KEY=secret-2
So how we can version control it?
One way to do it is by creating todo-compose/
folder and moving .env
and docker-compose.yml
files to todo-compose/
so we can initialize a git repo in it
$ mkdir todo-compose
$ mv docker-compose.yml .env todo-compose/
then we rename .env
to .env.example
and remove all passwords in it so we can use it as a reference. now todo-compose/
should be like this:
todo-compose
├── .env.example
└── docker-compose.yml
because we moved docker-compose.yml
file to its own folder, we will have to modify each service build and volume keys to move up one directory like this:
# todo/todo-compose/docker-compose.yml
version: '3.1'
# ...
todo-database:
volumes:
- ../todo-database:/docker-entrypoint-initdb.d/
# ...
todo-api:
build: ../todo-api
# ...
todo-worker:
build: ../todo-worker
# ...
but can we do better? it turns out that we can use symbolic links to make docker think that docker-compose.yml
exists in todo/
while in reality it is inside todo/todo-compose/
$ ln -s todo-compose/docker-compose.yml ./docker-compose.yml
now we copy our .env.example
reference file to todo/
$ cp todo-compose/.env.example ./.env
next we modify our newly created .env
to contain project passwords. now our directory structure should be like this:
todo
├── .env
├── docker-compose.yml -> todo-compose/docker-compose.yml
├── todo-compose/
│ ├── .env.example
│ └── docker-compose.yml
├── todo-api/
├── todo-worker/
└── todo-database/
One benefit that this method has is if you have multiple docker files like this:
todo-compose
├── .env.example
├── docker-compose.yml/
├── docker-compose.prod.yml/
└── docker-compose.dev.yml/
and you were issueing docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
in development and docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
in production. now you just have to do
# in development
$ ln -s todo-compose/docker-compose.yml ./docker-compose.yml
$ ln -s todo-compose/docker-compose.dev.yml ./docker-compose.override.yml
# in production
$ ln -s todo-compose/docker-compose.yml ./docker-compose.yml
$ ln -s todo-compose/docker-compose.prod.yml ./docker-compose.override.yml
then you can use the same command docker-compose up
for development and production
this post first appeared at https://ducaale.github.io
Top comments (0)