A logo showing the text blog.marcnuri.com
Español
Home»Operations»Docker container as a Linux system service

Recent Posts

  • Fabric8 Kubernetes Client 7.2 is now available!
  • Connecting to an MCP Server from JavaScript using AI SDK
  • Connecting to an MCP Server from JavaScript using LangChain.js
  • The Future of Developer Tools: Adapting to Machine-Based Developers
  • Connecting to a Model Context Protocol (MCP) Server from Java using LangChain4j

Categories

  • Artificial Intelligence
  • Front-end
  • Go
  • Industry and business
  • Java
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Tools

Archives

  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • February 2020
  • January 2020
  • December 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • July 2017
  • January 2017
  • December 2015
  • November 2015
  • December 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

Docker container as a Linux system service

2017-12-28 in Operations tagged DevOps / Docker / Linux / Portainer / Service / Systemctl / systemd by Marc Nuri | Last updated: 2021-03-19
Versión en Español

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.

docker as a linux systemd service
docker 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.

1docker run -d --name portainer --privileged -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /opt/docker/data/portainer:/data portainer/portainer

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.

1[Unit]
2Description=Portainer container
3After=docker.service
4Wants=network-online.target docker.socket
5Requires=docker.socket
6
7[Service]
8Restart=always
9ExecStart=/usr/bin/docker start -a portainer
10ExecStop=/usr/bin/docker stop -t 10 portainer
11
12[Install]
13WantedBy=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:

1systemctl start portainer
2systemctl stop portainer

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

1systemctl 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.

1[Unit]
2Description=Portainer container
3After=docker.service
4Wants=network-online.target docker.socket
5Requires=docker.socket
6
7[Service]
8Restart=always
9ExecStartPre=/bin/bash -c "/usr/bin/docker container inspect portainer 2> /dev/null || /usr/bin/docker run -d --name portainer --privileged -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /opt/docker/data/portainer:/data portainer/portainer"
10ExecStart=/usr/bin/docker start -a portainer
11ExecStop=/usr/bin/docker stop -t 10 portainer
12
13[Install]
14WantedBy=multi-user.target

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.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Comments in "Docker container as a Linux system service"

  • Avatar for Ed McGuigan
    Ed McGuigan
    2020-06-04 12:45
    Thanks for this Marc.

    I am just setting up a simple DNS server for a home lab and I wanted to do it with CoreDNS in a docker container. This was a great help.

    Having a little trouble getting the container to stop but I will try to figure that out. I am also going to work on having a template file so I can set up multiple instances as detailed at https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html.

    Cheers again,

    Ed.

Post navigation
Field injection is not recommended – Spring IOCAngular + Spring Boot integration using Gradle
© 2007 - 2025 Marc Nuri