How to make Docker Compose start on boot
Make your Docker containers start automatically after reboot. Learn why restart: always is the key and when you need a systemd unit for complex dependencies like network mounts.
Di short answer
Docker Compose services dey start wen system boot if two conditions dey met. Di Docker daemon must dey enabled as system service, and every service inside di file must get restart policy of unless-stopped or always. Add restart: unless-stopped to every service, run docker compose up -d one time, and di containers go start by demsef after reboot. Nothing else dey needed for normal cases.
You only need systemd unit if di order of how things start matter: like stack wey depend on mounted disk, VPN interface, or network share wey no ready wen Docker daemon start. Dat case dey real, and di second part of dis guide go explain am. If you still dey learn how service definitions and volumes dey work, start with di basics of Docker Compose on top VPS come back later.
Set di restart policy for inside compose.yaml
Di policy na one line per service. No global switch dey, so if you forget one service, e go stay down after di reboot while di rest of di stack go start.
services:
app:
image: nginx:1.27
restart: unless-stopped
ports:
- "8080:80"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:Apply am, den check di policy wey dey run for di container:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)Dat one go print unless-stopped. If e print no, e mean say you edit di file but you no recreate di container.
Dis na di common mistake wey pipo dey make. Di restart policy dey inside di container, e no dey inside di YAML file. If you edit compose.yaml, e no go change anything for container wey already exist. docker compose restart no go help too, because e go just stop and start di same container object without touch di configuration. Na only docker compose up -d go compare di file wit di containers wey dey run, notice say di policy don change, and recreate dem.
For container wey you no wan recreate now, change di policy for inside:
docker update --restart unless-stopped my-containerStill edit di YAML file too. docker update go change di live container, and di next docker compose up -d go read di file and put di old value back.
Wetin each restart value dey do
Docker get four values, and di difference between dem dey show only when di machine reboot or di daemon restart.
nona di default. Di container no dey ever restart automatically, no matter wetin happen.alwaysdey restart di container anytime e stop. If you stop am by yourself, e go still come back di next time wey Docker daemon start. Dis one dey surprise pipo sometimes: container wey you deliberately stop last week go just start again after reboot.unless-stoppeddey behave likealways, but if you stop container by yourself, e go stay stop even after daemon restart. Dis one na di value wey you need for service wey you dey occasionally off for maintenance.on-failuredey restart di container only when e exit wit non-zero exit code. You fit limit di attempts, like forrestart: on-failure:3.
For stack wey suppose dey up anytime di server dey up, unless-stopped na di correct default. Choose always only when you want container wey no go gree stay down.
Why restart: on-failure no dey survive reboot
Plenty pipo dey choose on-failure sake of say e sound like say e get sense, but dem go notice say all dia container don stop afta di first reboot. Di reason dey inside how dem define am. on-failure dey react to one tin only: di container process wey stop wit error code.
Reboot no be error. Wen di host shut down, systemd dey stop docker.service, and di daemon dey stop each container wit intention. Di container no fail, so di policy no get wetin e go react to. Wen di system dey come back up, di daemon dey look for containers wey e suppose resume, and on-failure container wey dem stop well-well no dey among dem. E go just stay for exited state.
You fit see am wit your eye. Set restart: on-failure for one service, run docker compose up -d, reboot, den run dis:
docker compose ps -aDem go list di service wit state of Exited and status like Exited (0) 2 minutes ago. Nothing spoil and nothing show as error for log, na dat one dey make am hard to sabi wetin happen. Di policy do exactly wetin e talk.
on-failure still get use. E fit container wey dey run job and fit crash, wey you want make e retry small time but no enter restart loop. E no be di correct tool to make long-running service dey work afta reboot.
Restart policies go work only if Docker service start for boot
Restart policies na Docker daemon dey enforce dem. If the daemon no start, nothing go fit enforce anything. Check am so:
systemctl is-enabled docker
systemctl is-enabled containerdBoth of dem suppose print enabled. The packages wey come from Docker official repository dey enable dem automatically when you install am, so for new server, this one usually dey correct. If any of dem print disabled, fix am like this:
sudo systemctl enable --now docker containerdOne trap dey here wey you suppose know. Ubuntu also get docker.socket, wey dey start the daemon immediately when something first talk to the Docker API. People go see say docker.socket dey enabled, come think say the daemon dey okay, then dem go disable docker.service sake of say dem wan save memory. When the system boot, nothing go call the API, so nobody go touch the socket, the daemon no go start, and no container go come up until you type your first docker command. Socket activation no be replacement for docker.service wey suppose dey enabled.
When systemd unit na better choice
Restart policies no get any way to arrange how dem dey start wit di rest of di system. Di daemon go start, and e go bring your containers up as soon as e fit. If your stack bind-mount a directory from a separate volume, an NFS (network file system) share, or an encrypted disk, di containers fit start before dat path dey ready. Docker go just create empty directory for di mount point and start di container inside, and your database go come up wit no data.
Write one systemd unit if any of dis tins happen. Di stack need a mount, a VPN interface, or another unit to ready first. You want systemctl stop myapp and systemctl start myapp to work di same way dem dey work for every other service for di box. Or you want make dem shut down di stack well-well during shutdown instead of make dem kill am wit di daemon. If systemd units still new for you, how to write systemd service and timer explain di file format well-well.
How to write di systemd unit
Put di stack for one fixed path wey no dey inside home directory. /srv/myapp na beta choice, sake of say unit wey dey run before anybody log in no get business dey read /home.
Create /etc/systemd/system/myapp.service:
[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetEnable and start am:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceOne healthy unit go show Active: active (exited). Dat one fit look like error di first time you see am. E correct: Type=oneshot wit RemainAfterExit=yes mean say di unit don run im command, di command don finish, and systemd still mark di unit as active so dat ExecStop go fit run wen you wan shutdown.
Every line get im reason. Requires=docker.service mean say di unit go fail sharp-sharp instead of make e dey run docker compose against one dead socket. After= set di order, sake of say Requires= alone no dey do dat one. RequiresMountsFor= make systemd pull di mount unit for dat path and wait for am, na dat one be di main reason wey you go take use unit instead of restart policy. TimeoutStartSec=0 stop systemd make e no kill di start job while one big image still dey download.
One small note about how to join di two ways. Docker documentation talk say make you no mix restart policies wit host process manager. Dat warning dey talk about process manager wey dey supervise di container process imself and dey restart am while di daemon dey try do di same tin. One Type=oneshot unit no dey supervise anytin, so e dey okay to keep restart: unless-stopped inside di compose file wit dis unit, and na wetin you want be dat. systemd go handle di ordering wen system boot, and di daemon go handle container wey crash for middle of night.
Verify wit one real reboot
Nothing fit replace real test. systemctl restart docker no dey test how mount dey arrange, and docker compose down follow by docker compose up -d no dey test anything about boot at all.
sudo rebootWait, connect back, and check for dis order:
uptime
systemctl is-active docker
docker compose psuptime go show say you dey look machine wey truly reboot. docker compose ps, wey you go run from di stack directory, suppose list every service as running wit uptime wey near di machine own. Any service wey show Exited na im you suppose check.
If something no start, di daemon log go show wetin happen for di boot time:
journalctl -u docker.service -b --no-pager | tail -50For stack wey unit dey manage, journalctl -u myapp.service -b --no-pager go show di exact docker compose output from boot, including if image pull fail or if .env file no dey.
Tin-tin wey dey secretly spoil auto-start
Containers wey dem create wit docker compose run no dey ever get di restart policy from di file. Compose dey treat dem like one-off containers. If one service dey behave like e no see im policy, check weda dem start am wit run instead of up.
One relative path inside volume or inside env_file entry dey resolve base on di compose file directory. Dat one dey work from your shell, and e dey work from unit wey set WorkingDirectory. E dey fail from unit wey no get one, because di working directory go come be /.
Rootless Docker na different matter. Di daemon dey run as user service, and user service dey stop wen di last session for dat user end. Enable am for di user and allow am make e continue to run even wen nobody log in:
systemctl --user enable docker
sudo loginctl enable-linger $USERWithout enable-linger, di rootless daemon go shut down wen you log out and di containers go follow am go, wey be like say di restart policy don spoil.
One last tin. Automatic security updates fit reboot server for fixed time, wey only good if your stack fit come back by imsef. To set dat one up for new machine na part of di work wey you suppose do for di first ten minutes on top new VPS.
FAQ
Wetin be di difference between restart: always and restart: unless-stopped?
Both dey restart di container if e stop by imsef. Dem dey different if you stop container wit your hand. If you use always, di container go start again once Docker daemon start, so if you reboot, your manual stop no go work again. If you use unless-stopped, di daemon go remember say you stop am purposely and e go leave am. Use unless-stopped except you really want container wey no go ever stay down.
I add restart: unless-stopped but di container still no dey start after reboot. Why?
Di policy dey inside di container, e no dey inside di file, and container wey don already exist no dey update if you just edit YAML. Run docker compose up -d make Compose fit recreate am, den check wit docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). If e print no, e mean say di container dey before you do your edit. Anoda common reason na say docker.service no dey enabled, you fit check am wit systemctl is-enabled docker.
I need systemd unit if I don already use restart policies?
Most times, no. Restart policy dey enough for stack wey only need network, wey be most stacks. Add unit if di containers depend on someting wey no ready when Docker daemon start, like external disk, encrypted volume, NFS share, or VPN interface. Di unit go give you ordering through After= and RequiresMountsFor=, wey restart policy no fit do.
How I go stop stack permanently make e no come back when I reboot?
If you use unless-stopped, docker compose stop dey enough, because if you stop container wit your hand, e no go start again when daemon restart. If you use always, just to stop am no go work because di container go return after reboot. Either you run docker compose down, wey go remove di containers, or you change di policy first wit docker update --restart no my-container. If systemd unit dey manage di stack, run sudo systemctl disable myapp.service join, or di unit go start am again.