Skip to main content

How to Run Containers at Startup

·3 mins
Docker Container Systemd
Ali Nasrolahi
Author
Ali Nasrolahi
I do some C stuff :)
Table of Contents

In this article, we’ll explore how to automatically run containers when the system boots up. While this could be achieved using orchestration frameworks like Kubernetes, that’s often too complex for local development environments or local CI/CD pipelines. To accomplish this, systemd services can simplify the process.

What is systemd and its services?
#

It’s quite likely that you’ve encountered systemd or at least heard about it before.
According to its Wikipedia:

systemd is a software suite that provides an array of system components for Linux operating systems. The main aim is to unify service configuration and behavior across Linux distributions. Its primary component is a “system and service manager” — an init system used to bootstrap user space and manage user processes.

For our purposes, it’s enough to know that systemd manages the system’s services. But what is a service?

In this context, a service refers to a daemon process that automatically starts when the operating system boots up. Some services are critical for the usability of a system, while others are less so. In Linux, most services end with the -d suffix, indicating that they are daemons (e.g., systemd, sshd). Whether critical or not, services make our lives much easier, as you’ll soon see!

That’s enough introduction, let’s get into it.

Let’s write a systemd service for our containers
#

If you are on a Linux machine, visit official systemd.service reference manual by issuing man systemd.service. Here, I’m not going to explain everything about systemd.service syntax; let’s keep that for another post. Nevertheless, if you’re interested, check out this article. Our exploration of services isn’t that deep, so you should be good to follow along anyway.

Let’s start by creating our service file. Name it whatever you like, just make sure it suffixed by .service. For instance: container.service

[Unit]
Description=Run my containers
Requires=docker.service <other services>.....
After=docker.service <other services>.......

[Service]
ExecStart=/usr/bin/docker run <whatever option you would normally set>
ExecStop=/usr/bin/docker stop <container>
Restart=no

By looking at this snippet, you should get the general idea!
In [Unit], we indicate what this service is and what it needs to be running properly (you could use whatever container provider you’d use instead of docker). If you’re confused by After and Requires see this. ExecStart and ExecStop are self-explanatory; Restart just says there’s no need to restart the service for example when it exits.

One idea is to use a custom script in place of the docker command in ExecStart= and ExecStop=. Or, my favorite option, just use docker compose and set everything up there. What I would do is create a /opt/personal/docker/service.yml file and modify container.service to something like this:

[Unit]
Description=Run my containers
Requires=docker.service
After=docker.service

[Service]
ExecStart=/usr/bin/docker compose -d -f /opt/personal/docker/service.yml up
ExecStop=/usr/bin/docker compose -f /opt/personal/docker/service.yml down
Restart=no

Now you could easily create a service file and a compose file and configure it however you like. Just a reminder, you can easily add multiple dependencies for your container. For example, let’s say you have your nginx and postgres containers configured on your testing server. But for them to work, you need to make sure the network is already online. So, just add network-online.target to your requirements.

Place the config files
#

Systemd’s system-wide services are usually placed inside /etc/systemd/system/, but you can specify user-specific services at ~/.config/systemd/ as well. Just note that enabling system-wide services requires superuser privileges. Move the files based on your preference.

Enable the service
#

The final step is enabling the services, so they run automatically after reboots.

Use the --now flag to start the service immediately.

For system-wide:

sudo systemctl daemon-reload
sudo systemctl enable container.service

And for users:

systemctl --user daemon-reload
systemctl --user enable container.service

That’s it! You’re all set.