Deploy a Docker Compose app
Last updated: June 21, 2026
If it runs with docker compose up on your laptop, it can run on a server. A docker-compose.yml is the most universal deploy shape there is — a web service, a worker, a database, a cache, all wired together — and it moves to American Cloud almost unchanged.
This recipe hands the whole job to your AI assistant. With the American Cloud MCP server connected, your assistant reads your compose file to understand what it's running, sizes a server to match, shows you the cost, creates the VM with the right ports open, installs Docker over SSH, brings the stack up, and puts it on your domain with HTTPS — from prompts you paste in.
Why Claude Code for this
Any MCP client can create the infrastructure. But deploying a compose stack also means running commands on the server: SSH in, install Docker, pull images, run docker compose up. Claude Code combines the American Cloud tools with your terminal, so a single session can provision the VM and deploy to it without switching tools. That's the setup this recipe assumes. Cursor and the other clients work too — you'll just run the SSH steps yourself when the assistant tells you to.
Provisioning is a write operation. You'll need a read-write API key from
console.americancloud.com/api-keys
and the --allow-writes flag on the MCP server. See the
overview for setup and safety details. Start read-only,
get comfortable, then switch the key when you're ready to build.
Before you start
- A working
docker-compose.ymlin a local git repo (the stack comes up withdocker compose upon your machine). - A list of which secrets your services need — database passwords, API keys. You'll hand the assistant the variable names; the values go straight onto the server.
- A domain you control, with the ability to point its nameservers or DNS at American Cloud.
- The MCP server connected to your assistant with a read-write key and
--allow-writes.
The one prompt that does it
Open your project in Claude Code and paste this, filling in the two placeholders. Read the cost estimate it shows you before approving anything.
I have a Docker Compose app in this repo. Deploy it to American Cloud on the
domain {your-domain.com}.
Plan it out first, then walk through it step by step:
1. Read docker-compose.yml to understand the services, the ports they expose,
the named volumes, and whether the stack includes its own reverse proxy
(Traefik, Caddy, nginx). Tell me what you found.
2. List the available regions and Ubuntu images, and the VM packages.
Recommend a size with enough CPU, memory, and disk to run all the services
in my compose file comfortably.
3. Check whether I already have an SSH key registered. If not, create one for
me and tell me where the private key is.
4. Show me a monthly cost estimate for the VM before creating anything. Wait
for me to confirm.
5. Create an Ubuntu VM with that SSH key, on an isolated network, and open
inbound ports 22, 80, and 443. Wait until it's fully running.
6. Over SSH: install Docker Engine and the compose plugin. Clone or copy this
repo to the server.
7. Create the server-side environment file. I'll give you the variable names
now and paste the secret values directly on the server — do not put real
secret values in this chat.
8. Bring the stack up with docker compose up -d and confirm every container
is healthy.
9. If my stack does NOT bring its own proxy, install nginx and reverse-proxy
it in front of the web service on port 80. If it DOES bring its own proxy,
point ports 80 and 443 straight at it instead.
10. Add a DNS A record pointing {your-domain.com} at the VM's public IP.
11. Once DNS resolves, set up HTTPS: run certbot for a Let's Encrypt
certificate if nginx is in front, or let the stack's own proxy handle ACME
if it has one.
Confirm the site is live at https://{your-domain.com} when you're done.What your assistant will do
Grounded in real MCP tools, here's the sequence:
- Read the compose file. It opens
docker-compose.ymlin your repo and works out the shape of the stack: which service faces the web and on what port, which services are internal-only (databases, caches, workers), what named volumes hold data, and whether the stack already ships a reverse proxy. That last point decides how HTTPS gets handled at the end. - Survey the options. It calls
list_regions,list_images(filtered to Ubuntu), andlist_vm_packagesto find a region near you, a current Ubuntu LTS image, and a compute tier with enough headroom for everything in the compose file at once — a multi-service stack needs more memory and disk than a single app. - Sort out the SSH key. It calls
list_ssh_keysto see what's already registered. If nothing fits,create_ssh_keygenerates a new pair — the private key is returned once and never stored, so your assistant saves it locally (for example to~/.ssh/) and sets the right permissions. It needs this key both to register on the VM and to SSH in afterward. - Price it first. It calls
get_cost_estimate_vmwith the exact region, package, size, and image — and shows you the hourly and monthly numbers before creating anything. Nothing is billed yet. - Create the server. On your go-ahead,
create_vmprovisions the Ubuntu VM. The same call carriesnetworkAccess.inboundPortsto open 22 (SSH), 80 (HTTP), and 443 (HTTPS), andkeypairsto install your SSH key. Opening ports throughcreate_vmsets up the firewall rule and the port forwarding together, so the ports are actually reachable — a firewall rule on its own would not be. The VM provisions asynchronously (statusCREATING→STARTED), so the assistant pollsget_vmuntil it'sSTARTEDwith a public IP. - Install Docker. Now in the terminal, it installs Docker Engine and the compose plugin on the VM, then clones or copies your repo onto the server.
- Write the server-side
.env. Your compose file references secrets by name; the assistant creates the environment file on the server and you paste the real values there. The variable names can appear in chat — the values never need to. This keeps credentials off your transcript and out of the repo. - Bring the stack up. It runs
docker compose up -don the server and checks that each container reaches a healthy state. Internal services (your database, cache, queue) talk to each other over the private compose network and are never exposed on the public IP — only the web port is. - Handle the front door. If your compose stack doesn't include its own proxy, the assistant installs nginx, which listens on port 80 and reverse-proxies to the web service's published port. If your stack does bring its own proxy (Traefik, Caddy, an nginx service), there's no second proxy — ports 80 and 443 route straight to it.
- Point the domain at it. It calls
list_dns_zonesto see if your domain is already hosted. If not,create_dns_zoneadds it (you'll then point your registrar's nameservers at American Cloud, which the assistant can show you). Thencreate_dns_recordadds anArecord for the domain pointing at the VM's public IP. - Turn on HTTPS. Once DNS resolves to the VM, it either runs certbot to obtain a Let's Encrypt certificate and reconfigures nginx for port 443 with auto-renewal, or — if your stack's own proxy speaks ACME — lets that proxy request and renew the certificate itself.
When it's done, you have your whole compose stack running over HTTPS on your own domain, on a server you own and can SSH into.
Tell the assistant to explain each step before it runs it if you want to follow along — "narrate what you're about to do and why." Destructive operations are flagged regardless, and clients that support confirmations will prompt you before anything irreversible.
Follow-up: redeploy after a change
Once it's live, shipping an update is one prompt:
I pushed new code. Redeploy the stack on the VM: SSH in, git pull the latest,
run docker compose pull and docker compose build for any changed services,
then docker compose up -d to recreate them. Confirm everything is healthy and
the site still loads.Ask the assistant to save this as a deploy script in the repo and every future update becomes "run the deploy script" — or just "redeploy the latest."
Follow-up: triage a misbehaving container
When something's wrong, describe the symptom and let the assistant investigate over SSH:
The api container keeps restarting. SSH into the VM and figure out why: check
docker compose ps for its state and restart count, then read the recent logs
with docker compose logs for that service. Tell me what's failing and propose
a fix before changing anything.The assistant reads the actual container state and logs from the server, so you get a diagnosis grounded in what's really happening rather than a guess.
Your data lives on the VM disk
Named volumes — your database files, uploaded content, anything a service writes to a mounted volume — live on the VM's disk. They survive docker compose down and restarts, but they're tied to that one server. Two things to set up early:
- Snapshots and offsite dumps for disaster recovery. See backups with your AI assistant — ask it to snapshot the VM before risky changes, and to set up a nightly job that dumps your database to object storage so a copy lives off the server.
- App file storage that you don't want filling the VM disk — user uploads, generated assets, large artifacts — belongs in object storage, with your service writing to it instead of a local volume.
Troubleshooting
Port 80 is already taken on the VM. If your compose stack publishes a service on port 80 and you also asked for an nginx proxy, they collide. Ask your assistant to "check what's bound to port 80 on the VM and pick one front door — either nginx proxying to the stack, or the stack's web service directly." You only want one thing listening on 80.
A container can't reach another service. Inside a compose network, services find each other by their service name, not localhost. If your app connects to localhost:5432 for a database that's a separate compose service, it won't resolve. Ask the assistant to "check the compose service names and make sure each service connects to the others by service name, not localhost."
The site is unreachable on port 80 or 443. Ask your assistant to "list the firewall rules on the VM's public IP and confirm 80 and 443 are open." It can call list_firewall_rules and check list_port_forwarding_rules — a port needs both a firewall rule (create_firewall_rule) and forwarding (create_port_forwarding_rule), or static NAT (enable_static_nat), to be reachable. Have it also confirm the VM is STARTED with get_vm and that the containers are healthy.
The disk is filling up. Pulled images, stopped containers, and dangling build layers accumulate fast. Ask your assistant to "SSH in and run docker system prune to reclaim space from unused images and containers," and if it keeps recurring, "resize the VM's disk with resize_vm_disk."
The domain doesn't load right after the DNS record is added. DNS changes take time to propagate — minutes to a couple of hours, depending on your registrar and the record's TTL. Ask your assistant to "check what {your-domain.com} currently resolves to and tell me when it points at the VM's IP." certbot (and any ACME proxy) needs DNS pointing at the VM before it can issue a certificate, so the HTTPS step may have to wait for propagation.
Next steps
- Scale out with a load balancer — run the stack on more than one VM behind a single IP
- Run on Kubernetes — when one compose host isn't enough
- Backups with your AI assistant — snapshots and offsite dumps for your volumes
- Object storage with your AI assistant — uploads, assets, and backup targets
- Write an AGENTS.md for your project — teach your assistant your deploy conventions