Docker Compose dey Start After Reboot: How to Set Am
Make Docker Compose services return after reboot with restart: always or unless-stopped. See why on-failure no survive one, and when systemd unit fit help.
Di short answer
Docker Compose services dey start when system boot if two conditions hold at the same time. Docker daemon must dey enabled as system service, and every service for the file must get restart policy of unless-stopped or always. Add restart: unless-stopped to every service, run docker compose up -d once, and the containers go come back by themselves after reboot. For the common case, nothing else dey required.
You only need systemd unit when order matter: for example, stack wey depend on mounted disk, VPN interface, or network share wey no ready when Docker daemon start. That case dey happen, and the second half of this guide cover am. If you still dey learn service definitions and volumes, start with Docker Compose basics for VPS and come back.
Set restart policy for compose.yaml
The policy na one line for each service. No global switch dey, so any service wey you forget go remain down after reboot, while the other services for the 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, then read the policy from the container wey dey run:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)That one go print unless-stopped. If e print no, you edit the file but you never recreate the container.
This na the most common failure. Restart policy dey stored for the container, no be for the YAML file. Editing compose.yaml no change anything for container wey already dey exist. docker compose restart no help too, because e only stop and start the same container object without changing the configuration. Na only docker compose up -d go compare the file with the containers wey dey run, notice say the policy change, then recreate dem.
For container wey you no want recreate now, change the policy inside the container:
docker update --restart unless-stopped my-containerStill edit the YAML file too. docker update go change the live container, but the next docker compose up -d go read the file and restore the old value.
Wetin each restart value dey really do
Docker get four values, and the difference between dem only show when machine reboot or daemon restart.
nona the default. Container no go restart automatically under any condition.alwaysgo restart container anytime e stop. If you stop am by hand, e go still come back next time Docker daemon start. This one dey often surprise people: container wey you deliberately stop last week don dey run again after reboot.unless-stoppeddey behave likealways, but container wey you stop by hand go remain stopped when daemon restart. Na this value you want for service wey you sometimes take down for maintenance.on-failurego restart container only when e exit with non-zero exit code. You fit limit the number of attempts, likerestart: on-failure:3.
For stack wey suppose dey up anytime server dey up, unless-stopped na the correct default. Choose always only when you want container wey no go easily remain down.
Why restart: on-failure no dey survive reboot
Plenty people dey pick on-failure because e sound careful, then dem discover say every container stop after the first reboot. The reason dey for the definition. on-failure dey react to only one thing: when container process exit with error code.
Reboot no be error. When host dey shut down, systemd dey stop docker.service, and daemon dey deliberately stop each container. Container no fail, so policy no get anything to react to. As system dey come back up, daemon dey check containers wey e need resume, and on-failure container wey stop cleanly no dey among dem. E remain for exited state.
You fit see am directly. Set restart: on-failure for one service, run docker compose up -d, reboot, then run:
docker compose ps -aThe service go show with state of Exited and status like Exited (0) 2 minutes ago. Nothing spoil and nothing dey logged as error, na wetin make this matter hard to diagnose. The policy do exactly wetin e talk.
on-failure still useful. E fit work for container wey dey run one job and fit crash, where you want limited number of retries and no restart loop. But e no be the correct tool to keep long-running service alive across reboots.
Restart policy go only work if Docker service start for boot
Docker daemon dey enforce restart policy. If daemon no start, nothing go enforce anything. Check am:
systemctl is-enabled docker
systemctl is-enabled containerdBoth suppose print enabled. Packages from Docker official repository dey enable dem when you install dem, so for fresh server this check usually pass. If any one print disabled, fix am:
sudo systemctl enable --now docker containerdOne trap dey here wey you need understand. Ubuntu also ship docker.socket, wey start daemon on demand the first time something talk to Docker API. People see say docker.socket dey enabled, assume say daemon dey covered, then disable docker.service to save memory. For boot, nothing dey call API, so socket no dey touched, daemon no start, and no container come up until you type your first docker command. Socket activation no be replacement for enabling docker.service.
When systemd unit na the better answer
Restart policies no get any idea of ordering against the rest of the system. The daemon go start, then e go bring your containers up as soon as e fit. If your stack bind-mounts a directory from separate volume, an NFS (network file system) share, or encrypted disk, the containers fit start before that path dey available. Docker go create empty directory for the mount point without wahala, then start the container with am, and your database go come up without data.
Write systemd unit when any of these conditions apply. The stack need mount, VPN interface, or another unit to become ready first. You want systemctl stop myapp and systemctl start myapp to work the same way dem work for every other service for the machine. Or you want the stack to shut down cleanly when the system dey shut down, instead of dem killing am together with the daemon. If systemd units new to you, how to write systemd service and timer explain the file format with more detail.
Writing the systemd unit
Put the stack for one fixed path wey dey outside home directory. /srv/myapp na good choice, because unit wey dey run before anybody log in no get business to 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.serviceHealthy unit go show Active: active (exited). E fit look wrong the first time you see am. But na correct result: Type=oneshot with RemainAfterExit=yes mean say the unit run im command, the command finish, and systemd keep the unit marked active so ExecStop go run for shutdown.
Every line get reason. Requires=docker.service mean say the unit go fail fast instead of running docker compose against dead socket. After= set the order, because Requires= by itself no do that. RequiresMountsFor= make systemd pull in the mount unit for that path and wait for am. Na the main reason to use unit instead of restart policy. TimeoutStartSec=0 stop systemd from killing the start job while e still dey pull large image.
Make we talk about how to combine the two mechanisms. Docker documentation advise against mixing restart policies with host process manager. That warning concern process manager wey dey supervise the container process itself and restart am while the daemon dey try do the same thing. A Type=oneshot unit no dey supervise anything, so you fit keep restart: unless-stopped for the compose file together with this unit. Na wetin you want. systemd handle the order during boot, while the daemon handle container wey crash for 3 in the morning.
The unit go look different when na plain long-running process you dey keep alive instead of stack. For that case, no daemon dey underneath am, so systemd own Restart= gots to do the supervision; running dsh headless behind systemd na worked example of this arrangement, including the dedicated user and the journal.
Real reboot pawa verify am
Nothing fit replace the real test. systemctl restart docker no dey test mount ordering, and docker compose down wey you run after docker compose up -d no dey test anything about boot at all.
sudo rebootWait, reconnect, then check am for this order:
uptime
systemctl is-active docker
docker compose psuptime confirm say na machine wey really reboot you dey look at. docker compose ps, wey you run from stack directory, suppose list every service as running, with uptime wey dey close to the machine own uptime. If service show Exited, na that one you suppose inspect.
If anything no come up, daemon log cover the boot window:
journalctl -u docker.service -b --no-pager | tail -50For stack wey unit dey manage, journalctl -u myapp.service -b --no-pager show the exact docker compose output from boot, including failed image pull or missing .env file. The reboot wey you schedule na the one you dey monitor, so make the unit tell you about the ones wey you no dey watch: an OnFailure= line wey point to self-hosted ntfy server go turn stack wey fail to come back into push notification, instead of something you discover after many days.
Wetin dey quietly spoil auto-start
Containers wey you create with docker compose run no dey get restart policy from the file. Compose dey treat dem as one-off containers. If service look like say e dey ignore the policy, check whether you start am with run instead of up.
Relative path for volume or inside env_file entry dey resolve against the compose file directory. E dey work from your shell, and e dey work from unit wey set WorkingDirectory. E no dey work from unit wey no get am, because working directory go be / then.
Rootless Docker na separate case. Daemon dey run as user service, and user service dey stop when the last session for that user end. Enable am for the user, and allow am to keep running when nobody dey logged in:
systemctl --user enable docker
sudo loginctl enable-linger $USERWithout enable-linger, rootless daemon go shut down when you log out, and the containers go follow am. This one go look exactly like say restart policy spoil.
One last thing. Automatic security updates fit reboot server for fixed hour. This one good only if your stack fit come back by itself. Setting am up for new machine belong with the rest of the first-hour work inside the first ten minutes for new VPS.
FAQ
Wetin be di difference between restart: always and restart: unless-stopped?
Both of dem restart di container when e stop by itself. Di difference dey happen after you stop container by hand. With always, di container start again di next time Docker daemon start, so reboot go cancel your manual stop. With unless-stopped, di daemon remember say you stop di container deliberately and leave am like that. Use unless-stopped unless you specifically want container wey no go stay down.
I add restart: unless-stopped but di container still no start after reboot. Why?
Di policy dey for di container, e no dey for di file, and editing YAML no update container wey already dey exist. Run docker compose up -d make Compose recreate am, then confirm with docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). If e print no, di container don exist before you edit am. Another common cause na docker.service wey no dey enabled. You fit check am with systemctl is-enabled docker.
I need systemd unit if I already dey use restart policies?
Most times, no. Restart policy dey enough for stack wey only need network, and na most stacks be that. Add unit when di containers depend on something wey no ready when Docker daemon start, like external disk, encrypted volume, NFS share, or VPN interface. Di unit give you ordering through After= and RequiresMountsFor=, wey restart policy no fit express.
How I go stop stack permanently make e no come back for di next reboot?
With unless-stopped, docker compose stop dey enough, because container wey you stop by hand no go resume when daemon restart. With always, stop no dey enough and di container go return after reboot. Either run docker compose down, wey remove di containers, or change di policy first with docker update --restart no my-container. If systemd unit dey manage di stack, run sudo systemctl disable myapp.service too, otherwise di unit go start am again.