Meta

Docker container as a Linux system service

Introduction

There are many different ways to orchestrate docker container management, initialization, deployment, etc. Even Docker brings its own vanilla tool and mode. There are also many other third party container orchestration tools such as Kubernetes, Rancher, Apache Mesos, etc.

Docker daemon offers simple means to start, stop, manage and query status of deployed containers. In this post we’ll cover how to use a docker + systemd only approach to deploy containers as Linux services without the need for third party tools or complex deployment descriptors.

In this tutorial we will show how to deploy Portainer as a Linux systemd service.

Existing container

The simplest method to deploy a container as a service is to create a docker container with a given name and then map each of the docker operations (start and stop) to systemd service commands.

Once we’ve created this container we can start, stop and restart the container using the regular docker commands by indicating the container name (docker stop portainer, docker start portainer, docker restart portainer).

We can create a new systemd unit file with the service description by creating a new file in /etc/systemd/system/. For the sake of this example we’ll create a new portainer.service file with the following contents.

/etc/systemd/system/portainer.service

1

2

3

4

5

6

7

8

9

10

11

12

13

[Unit]

Description=Portainer container

After=docker.service

Wants=network-online.target docker.socket

Requires=docker.socket

[Service]

Restart=always

ExecStart=/usr/bin/docker start-aportainer

ExecStop=/usr/bin/docker stop-t10portainer

[Install]

WantedBy=multi-user.target

The unit file creates a new service and maps docker start and docker stop commands to the service start and stop sequences.

The unit file describes as dependencies the network-online target and the docker socket, if docker socket doesn’t start this service won’t either. Also adds a dependency to docker.service, so this service won’t run until docker.service has started.

We now can start / stop the service by issuing the corresponding command:

Start / Stop portainer service

Shell

1

2

systemctl start portainer

systemctl stop portainer

We can also install the service to run at start up by running:

Enable new portainer service

Shell

1

systemctl enable portainer.service

Create container if applicable on start

We can improve the previous unit file to create the container if it doesn’t exist, this will allow us to skip the first step of manually creating the container (docker run…) and can be used to deploy containers by simply creating the service descriptor unit file.

We simply added an additional line with an ExecStartPre instruction. Systemd unit files have certain limitations when executing commands in the ExecStart, ExtecStop, ExecStartPre…. definitions. In order to overcome these limitations we encapsulate the command inside a “/bin/bash -c” command that allows us a little more flexibility.

The first command (docker container inspect) will check to see if a container with this name already exists in the docker host machine. If this command throws an error (i.e. container doesn’t exist) the next instruction will be run. This command is the same as we used previously to create the container manually.

It is really important to execute the docker run command with the -d (detached) flag, not doing so will prevent the command to return thus not allowing the next step (ExecStart) to run, so the systemd will never become active.

Depending on the previous existence of the container the service start sequence will behave one way or another:

A container named portainer already exists:

ExecStartPre will return with no error on the first command (docker container inspect…) thus won’t execute the latter (docker run…).

ExecStart will run normally and the process will attach to the container.

A container named portainer doesn’t exist:

ExecStartPre will return with an error on the first command (docker container inspect…), the second command will create and start the container in detached mode returning the control to systemctl.

ExecStart will run (docker start) which will not have any effect but will attach to the running container.

Conclusion

In this tutorial we’ve shown a simple way to deploy docker containers as Linux systemd services. This is a “cheap” alternative to Kubernetes or any other container orchestration tool, although it’s not as reliable and should only be used in very specific cases.