How to version control docker-compose file
Tags: docker
Published: 2019-06-29 18:10:00


At some point docker-compose file gets complicated and we need to version control it. the typical directory structure looks like this:

```bash
todo
├── .env
├── docker-compose.yml
├── todo-api/
├── todo-worker/
└── todo-database/
```

and our `docker-compose.yml` file is like this:
```yaml
# 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
```bash
$ 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:
```yml
# 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/`
```bash
$ ln -s todo-compose/docker-compose.yml ./docker-compose.yml
```

now we copy our `.env.example` reference file to `todo/`
```bash
$ 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
```bash
# 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*