Let's say we have the following docker-compose.yml
:
version: '3'
services:
db:
image: "postgres"
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=mysecretpassword
web:
build: web
depends_on: [ db ]
ports:
- "80:80"
The first service, db
, just runs a container with the official postgres image from Docker Hub.
The second service, web
, first builds a new image based on the Dockerfile
in a folder also called web
, then runs a container with that image.
While developing, we now can (repeatedly) make changes to whatever is in the web
folder, then run docker-compose up --build
to run our app locally.
Let's say we now want to deploy to production. My understanding is that docker-compose.yml
can now be used to "define a stack in Docker's swarm mode" (see this answer, for instance). However, for the build
step of the web
service, Docker's compose file documentation states that
This option is ignored when deploying a stack in swarm mode with a (version 3) Compose file. The docker stack command accepts only pre-built images.
It probably wouldn't be a great idea to build the image on the production machine anyways, as this would leave build artifacts (source code) behind; this should happen on a build server.
My question is, is there a recommended way to modify docker-compose.yml
en route to production to swap out build: web
with image: <id>
somehow?
Nothing on Use Compose in production on that. Is there something wrong with my approach in general?
docker-compose.yml
should only contain canonical service definitions.
Anything that's specific to the build environment (e.g. dev vs prod) should be declared in a separate file docker-compose.override.yml
. Each build environment can have its own version of that file.
The build: web
declaration doesn't belong into docker-compose.yml
, as it's only supposed to run locally (and possibly on a build server), not in production.
Therefore, in the example above, this is what docker-compose.yml
should look like:
version: '3'
services:
db:
image: "postgres"
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=mysecretpassword
web:
depends_on: [ db ]
ports:
- "80:80"
And this would be the default docker-compose.override.yml
for local development:
version: '3'
services:
web:
build: web
Running docker-compose up --build -d
will now build the latest code changes and launch our app locally.
There could also be another version docker-compose.override.build.yml
, targeting a build/CI server:
version: '3'
services:
web:
build: web
image: mydockeruser/web
Running docker-compose -f docker-compose.yml -f docker-compose.override.build.yml push
will build the latest code changes and push the image to its registry/repository.
Finally, there could be another version docker-compose.override.prod.yml
:
version: '3'
services:
web:
image: mydockeruser/web
Deploying to production (just to a single Docker host, not a cluster) can now be as simple as copying over only docker-compose.yml
and docker-compose.override.prod.yml
and running docker-compose -f docker-compose.yml -f docker-compose.override.prod.yml up -d
.
The correct way to do it (i.e. the way I do it :P) is to have different docker-compose files; for example, docker-compose.dev.yml
and docker-compose.prod.yml
. You can then push your production-ready image to a repository, say Docker Hub, and reference that image in docker-compose.prod.yml
's web
service. All the while you can use the dev docker-compose file (the one with the build
option) for local development.
Also, in case you've thought about this, you cannot use env variables as keys in docker-compose (see here). So there is no way to conditionally set either image
or build
options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With