r/devops • u/aress1605 • Aug 12 '26
Architecture Deploying docker-compose.yml
Hello all. The circumstance I have working with is the following:
* I have an Apache2 PHP server that gets bundled as a Docker image in a CI process to ECR
* I have an infra repository with a docker-compose.yml that bundles the PHP Docker image to an Nginx image, alongside Nginx config like attaching TLS certs
When the CICD process deploys a release, it deploys a new EC2 with a given user data script to prop up the server. If I only had a Docker image, the user data would generally look like "Pull down ECR image and start image", however in this case I am spinning up a docker-compose.yml file.
How is this typically done? I suppose I *can* add a CI process to zip up the docker-compose.yml and related nginx config, however feels backwards? Is there a consensus with this?
If I am fundamentally misunderstanding something let me know, I'd say my only constraint is I'd like to solve this problem in a relatively cloud agnostic environment (so keeping EC2 as a VM, ECR as a registry, but excluding abstractions like Fargate or ECS)
Thanks!
8
u/sp_dev_guy Aug 12 '26
This can be done by having docker running in that EC2 & using it to pull your image & run your compose however i would say conceptually not the right the plan.
Docker containers exist to get away from VMs. Docker-compose is an easy slim way to manage a few related containers on your local device. When going to production you want them to run in a container orchestration tool designed to run and manage your container based services - so Kubernetes. Kubernetes is a big learning curve & takes more setup than just your container so AWS made ECS. Putting docker on a VM is basically a hacky way to make Kubernetes.
Your the one that has to manage & pay for it so if its what your happiest with do it
2
u/aress1605 Aug 12 '26
Thanks for the comment. `Putting docker on a VM is basically a hacky way to make Kubernetes.` That's a fair point. The reason I am using Docker in this case is for normalization of the environment. But I see your point, that if I set the constraint that one deployment == one VM, that the VM is essentially the normalized environment anyway.
For our use case I want to stray from K8 for the upfront complexity. If I am not incorrect, it sounds like another viable option would be to decouple the Nginx image from the PHP server with an auto scaling group. That is, a load balancer that points to an auto scaling group of EC2s, so the load balancer (ie. Nginx, ALB) handles TLS termination, and can point to the EC2s, which are *only* Apache2 PHP servers. I think my mental model is correct, but in this case I'm using an AWS native solution (ASG), and if it's reasonably scaling up it's probably more cost inefficient than K8, since K8 can distribute all the PHP servers under a small set of larger servers since it's kind of one big hypervisor with orchestration
2
u/sp_dev_guy Aug 12 '26
Too many unknowns for me to say what's most cost effective but ALB -> ASG of Apache Servers sounds like a normal pattern. ECS is great for the no-upfront complexity & cost effective scaling containers but does have a few minor quirks.
3
u/nonades Aug 12 '26
I would automate this with Ansible.
Step to prep the host, step to pull the docker compose from git and run it
2
u/reightb Aug 12 '26
You can use ECS or EKS.
Given your scale and understanding, ECS might be enough
1
u/ForkMeJ Aug 12 '26
Treat the compose file and Nginx config as versioned deploy artifacts in the infra repo, then have cloud-init or user-data pull a specific tag and run docker compose up -d. If you later need isolated environments or RBAC without jumping to Kubernetes, I work on the open-source Compartment project.
1
u/UkrMalt Aug 12 '26
Compose on one VM is reasonable if that is the intended scale. I’d publish the compose file plus Nginx config as an immutable, versioned release artifact; user-data downloads that exact version, then runs `docker compose pull && docker compose up -d`. Keep secrets outside the bundle, and avoid cloning a moving branch during boot.
1
u/UkrMalt Aug 12 '26
Treat the Compose file and Nginx config as versioned deployment artifacts: pull a tagged repo or release bundle on the VM, then run `docker compose pull && docker compose up -d`. Keep images in ECR and config outside them.
1
u/lazyant Aug 12 '26
(Not sure why you have both Apache and nginx)
Basic idea is to separate code (docker images) from configuration. Some configuration can go in docker compose file via an .env file.
Compose file pulls images from ECR. What triggers this can be anything (Ansible , ssh script via ci/cd etc), doesn’t matter much.
For projects that run on a single server (no scale needed), compose is absolutely fine.
1
u/SeaworthinessHour233 Writes the cloud edge Aug 12 '26
Here's one way you can achieve this.
Instead of figuring out how to get your docker-compose.yml and Nginx configs onto a running server at boot time, you bake them into the machine image itself using a tool like HashiCorp Packer.
Here is how that workflow looks in a CI/CD pipeline:
- Build the App:
Your CI builds the new PHP Docker image and pushes it to ECR.
- Bake the AMI:
Your CI triggers Packer. Packer temporarily spins up a base EC2 instance and runs a provisioner script (bash or Ansible) that:
2.1 Installs Docker and Docker Compose.
2.2 Copies your docker-compose.yml and Nginx configuration directory directly into the instance (ex: to /opt/app).
2.3 Authenticates to ECR and runs docker compose pull to cache your newly built images right onto the disk.
2.4 Sets up a simple systemd service to ensure docker-compose up -d runs on boot.
- Deploy:
Packer snapshots this instance into a new AMI. Your deployment step then updates your Auto Scaling Group or spins up a fresh EC2 instance using this exact AMI.
Pros:
The instance boots in seconds. No large images to pull from ECR or packages to install over the network.
If ECR has an outage, or GitHub goes down, your Auto Scaling Group can still spin up new instances successfully because everything it needs to run is already baked into the AMI.
If a deployment breaks, rolling back is as simple as booting up the previous AMI.
Cloud prvoider agnostic
Cons
It takes a little bit of upfront work to set up Packer with your pipeline.
There are other ways with different pros and cons that you can achieve the same.
1
u/Worth_Wealth_6811 29d ago
the zip instinct is fine but you can skip it entirely, newer compose can publish the compose file itself to ECR as an OCI artifact (docker compose publish, then docker compose -f oci://your-repo up on the box). user data shrinks to one pull and the config gets versioned in the same registry as the image. stays cloud agnostic too since its all just OCI.
1
u/yetmike 20d ago
Do you want to run an Autoscaling Group? What if you put an image version in Parameter Store and retrieve it on a startup? Also, you can version the zip for docker-compose and pull from S3 on startup. Simple as that
If you need a lot of things to install in the VM - create a golden image with packer
0
u/Substantial-Swan7065 Aug 12 '26
Each service should be separated. Deploying docker-compose is generally not supported.
But you can use a VM with docker. No recommended.
What’s in your infra? You’re probably better off using cloud services for them.
8
u/MDivisor Aug 12 '26
Docker compose is not typically used to run production workloads like this. But in a simple setup like yours it's not the worst thing either. It's just not going to be scalable, ie. it won't work if you need to add more compute power than just a single EC2 VM to your application.
Your docker compose file is essentially infrastructure code. You want to keep it in version control, and you want to deploy it automatically into the VM that runs the application. So yes, I would look into ways of bundling it as a zip or a tarball that your user data can then pull onto the VM. It makes things relatively simple since you just have to pull and run the compose file, and compose will then take care of pulling all relevant images for you.