I have an Apache/PHP application packaged as a Docker image and pushed to ECR by CI. A separate infrastructure repository contains a docker-compose.yml that runs the PHP container alongside Nginx, including the Nginx configuration and mounted TLS certificates.
For each release, the deployment creates a new EC2 instance and uses user data to initialize it. With a single image, the process would be to install Docker, pull the image, and start it. Since this deployment also needs the Compose file and Nginx configuration, what is the usual way to deliver and run those files?
Would it be reasonable to package the Compose file and related configuration as a versioned archive or release artifact, have user data download it, and run Docker Compose? Or is there a more established approach, such as Ansible or separating Nginx from the application behind a load balancer?
I'm intentionally keeping the setup relatively cloud-agnostic: EC2-style virtual machines and a container registry are fine, but I'd prefer not to use ECS, Fargate, Kubernetes, or another orchestration platform for now.
4 Answers
Compose is primarily a single-host tool, so it does not provide scheduling or failover across multiple machines. You could put several identical VMs behind a load balancer, with each VM running its own Compose stack, but then you need to handle health checks, rolling updates, shared storage, and configuration consistency yourself.
A simpler architecture may be to terminate TLS at the load balancer and run only the Apache/PHP container on the application instances. That removes the need to run Nginx on every VM. If the application eventually needs coordinated scheduling, service discovery, or more advanced scaling, an orchestrator becomes more attractive—but it is not required for a modest single-VM deployment.
Avoid assuming that building a new VM for every release is the only deployment model. You can use an immutable image or a startup script that retrieves a pinned release, but make sure the application image, Compose file, and Nginx configuration are all tied to the same release identifier. Otherwise a new VM could accidentally combine an old configuration with a newer application image.
For a small, single-host deployment, keeping Docker Compose is perfectly workable. Treat the Compose file and Nginx configuration as versioned infrastructure artifacts in your infrastructure repository. During boot, install Docker, download a specific release or tag—not a moving branch—and run `docker compose pull && docker compose up -d`.
You can package the files as a tarball or zip, or download them from version control or an artifact store. The important part is that the deployment is immutable and reproducible. Keep secrets out of the bundle and provide them through a secret manager, environment configuration, or mounted files.
You can also automate the host setup with Ansible: prepare the VM, install Docker and the Compose plugin, copy or download the selected Compose release, install the Nginx configuration and certificates, then start the stack. Whether you use Ansible, cloud-init, or a release archive is mostly an operational choice; keep the deployment definition in version control either way.

That makes sense. I was mainly concerned that packaging the Compose file and Nginx configuration felt unusual, but treating them as versioned deployment artifacts seems straightforward for this scale.