4 Important Services Everyone Should Deploy In A Docker Swarm
Tags: programming, devops, beginners, docker
Published: 2023-07-29 08:28:58

**Enhance your Docker Swarm with four important services that you will love: Traefik, Portainer, Registry, FTP**

In a previous article, I showed [how a Docker Swarm is set up in ~15 minutes](https://www.paulsblog.dev/docker-swarm-in-a-nutshell/).

**Remember:** Whenever you read “Docker Swarm” we are talking about “Docker in Docker Swarm mode”

Within this article, I will show and explain four services everyone should use in their Docker Swarm: **traefik**, **Portainer**, **Docker-Registry**, **FTP**.

Traefik
-------

> _Makes Networking Boring_
> _Cloud-Native Networking Stack That Just Works._

One important service in any Docker Swarm is [traefik](https://www.traefik.io/?ref=paulsblog.dev). I use this for assigning domains/subdomains to every docker service. A simple docker-compose.yml can be found on my FTP [here](https://ftp.f1nalboss.de/data/docker-compose.traefik.yml?ref=paulsblog.dev). To use this file it is necessary to update the labels for your docker swarm by executing:

```bash
export NODE_ID=$(docker info -f '{{.Swarm.NodeID}}')
docker node update --label-add traefik-public.traefik-public-certificates=true $NODE_ID
```

These two commands assume that you want to have traefik on your manager node! To understand what a manager node [have a look at the docker documentation](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/?ref=paulsblog.dev).

Another **VERY** important step is creating a file named `acme.json` within the folder where the docker-compose will be stored and do:
```bash
chmod 600 acme.json
```

Furthermore, you have to define a `TRAEFIK_USERNAME`, `TRAEFIK_HASHED_PASSWORD`, `TRAEFIK_DOMAIN` and `TRAEFIK_SSLEMAIL`:

```bash
export TRAEFIK_USERNAME=admin
export TRAEFIK_HASHED_PASSWORD=$(openssl passwd -apr1 testpassword)
export TRAEFIK_DOMAIN=dashboard.YOUR_DOMAIN.tld
export TRAEFIK_SSLEMAIL=your_email@address.de
```

The last command which has to be executed before you can deploy the service is creating the `traefik-public` network which will be used across all containers:
```
docker network create --driver=overlay traefik-public
```

After this is done it is possible to deploy traefik to your docker swarm:
```
docker stack deploy -c docker-compose.traefik.yml
```

Just test it by launching `https://dashboard.YOUR_DOMAIN.tld`. This domain will use an SSL certificate and you can log in with **_admin/testpassword_** if you followed the above instructions.

Portainer
---------

> _Portainer is a powerful, GUI-based Container-as-a-Service solution that helps organizations manage and deploy cloud-native applications easily and securely._

My docker-compose.yml for Portainer is not special. You can find it [here](https://ftp.f1nalboss.de/data/docker-compose.portainer.yml?ref=paulsblog.dev) __if you need it__. Normally there are no differences between my and the one you find if you google for Portainer service.

If you use mine you have to ****add a label to your manager!**** ****This is very important**** because Portainer needs a connection to your docker socket. You can add the label with these commands:

```bash
export NODE_ID=$(docker info -f '{{.Swarm.NodeID}}')
docker node update --label-add portainer.portainer-data=true $NODE_ID
```

Furthermore declare the `PORTAINER_DOMAIN` which will be used.
```bash
export PORTAINER_DOMAIN=portainer.$PRIMARY_DOMAIN
```

After you have done this you can deploy Portainer:
```bash
docker stack deploy -c docker-compose.portainer.yml portainer
```

**Timing Note:** Make sure you log in and create your credentials soon after Portainer is ready, or it will automatically shut down itself for security. If you didn’t create the credentials on time and it shut down itself automatically, you can force it to restart with:

```bash
docker service update portainer_portainer --force
```

Docker Registry
---------------

The Registry is a stateless, highly scalable server side application that stores and lets you distribute Docker images. The Registry is open-source, under the permissive Apache license.

The docker registry is important to have in a swarm environment if you don't want to upload your code/data to a public registry. If you don't have any private registry for yourself you have to upload the resulting image to docker-hub each time you extend an official image with your application code. While this is great if you only want to extend functionality in a general way it is not advisable if you copy your website with your closed source code into the image.

Imagine you have a modified Nginx image where the HTML folder is copied into the image:

```docker
FROM nginx
COPY html /usr/share/nginx/html
```

In many cases the `html` folder will contain your website which you don't want someone to use. If you have a private registry you can build the image and upload it but if you don't have one you have to use a public registry.

**That's why I think that every swarm needs to have access to a private registry.** And that's why I created one. Normally you can set up a registry with a simple `docker run` command but I wanted to have a registry that can be reached from everywhere. Because of that, I created a registry that could be accessed by username and password.

My personal `docker-compose.yml` can be downloaded [here](https://ftp.f1nalboss.de/data/docker-compose.registry.yml?ref=paulsblog.dev). It will run within my Traefik environment and create a new registry that I can use from every node in my swarm. To deploy the service you have to declare some environment variables which are used while deploying: `REGISTRY_USERNAME`, `REGISTRY_HASHED_PASSWORD` and `REGISTRY_HOST`:

```bash
export REGISTRY_USERNAME=reg_adm
export REGISTRY_HASHED_PASSWORD=$(openssl passwd -apr1 regsupersecret)
export REGISTRY_HOST=reg.YOUR_DOMAIN.tld
```

With these variables you can deploy the service:

```bash
docker stack deploy -c docker-compose.registry.yml registry
```

To use the registry you have to do two more things. The first thing is a quality of life feature to easily change the domain of the registry without affecting every container which uses an image from the private registry. ****Add DOCKER\_REGISTRY to .profile for**** `****root****` ****or whatever user you are using so it is known if docker-compose files should be pushed/downloaded****. The second task is very important. You have to execute `docker login` on every node of your swarm so that every node is allowed to pull images.

Now you can start using your private registry in docker-compose.yml. If you created a `DOCKER_REGISTRY` environment variable you can use it like this in your docker-compose.yml:

```yaml
myapp:
    image: ${DOCKER_REGISTRY}/simple-app
    build:
      context: ./
      dockerfile: Dockerfile
```

If you want to deploy a service that contains the above part you have to build and push your image to deploy it correctly:
```bash
docker-compose build
docker-compose push
docker stack deploy -c docker-compose.yml www
```

If you only build and deploy it other nodes in your swarm cannot pull the image and so the service cannot be deployed.

FTP
---

Another important service is an FTP server for saving files you want to use anywhere in any app or service you create while working with the swarm.

I decided to use “pure-ftp” because it was the one I found while googling for a nice FTP server that runs within a docker environment. Because I want to keep it simple I created an FTP server without any `traefik` configuration. **BUT** I did a simple trick in putting the FTP server together with a website in a docker-compose.yml. I have done this because I want to have the possibility to download files from the server over `https` which I uploaded with `ftp`.

After configuration, I came up with [this docker-compose.yml](https://ftp.f1nalboss.de/data/docker-compose.web.yml?ref=paulsblog.dev). Within the file, you can see that I use a custom Dockerfile for the web service which contains:
```docker
FROM nginx
COPY html /usr/share/nginx/html
```

The trick I describe is just the defined volume within the `docker-compose.yml`. As you can see I defined data in both services. Within `web-service` I just have an extra folder within the Nginx HTML folder so that I can access it from the web. And within the `ftp-service` I define data as the place where user can upload their data.

Before it is possible to deploy this service you have to declare environment variables: `FTP_USERNAME`, `FTP_PASSWORD`, `FTP_DOMAIN_FOR_CERT`, `FTP_ORG_FOR_CERT`, `FTP_COUNTRYCODE_FOR_CERT` and `WEBSERVICE_DOMAIN`:
```bash
export WEBSERVICE_DOMAIN=www.MYDOMAIN.tld
export FTP_USERNAME=SUPERUSER
export FTP_PASSWORD=clearTextPW
export FTP_DOMAIN_FOR_CERT=$WEBSERVICE_DOMAIN
export FTP_ORG_FOR_CERT=mybusiness
export FTP_COUNTRYCODE_FOR_CERT=DE
```

Furthermore, you have to add a label to any node of your swarm. To achieve this use `docker node ls` to find out the ID from every node and execute:
```bash
docker node update --label-add www.ftp-data=true ID_OF_NODE_TO_USE
```

After this is done you can safely deploy your website with enabled FTP

```bash
docker stack deploy -c docker-compose.web.yml webandftp
```

Now it is possible to connect with an [FTP client](https://filezilla-project.org/?ref=paulsblog.dev) to your `WEBSERVICE_DOMAIN` and upload a file (`test.txt`) which then can be accessed by this URL: `WEBSERVICE_DOMAIN/data/test.txt`

The whole FTP docker service can be downloaded from my GitHub:

https://github.com/paulscode-de/ftp-server

It is very important to say that you only can connect with your WEBSERVICE\_DOMAIN if this domain also has an A record to your manager node!

5. Closing Notes
-----------------

I hope you find this article helpful and can use my provided files to set up these services within your own Docker Swarm.

In my humble opinion, these four services should be present in every Docker Swarm environment because they are mandatory (or exchanged with function-like services).

This article was originally published on my blog at [https://www.paulsblog.dev/services-you-want-to-have-in-a-swarm-environment/](https://www.paulsblog.dev/services-you-want-to-have-in-a-swarm-environment/)

Feel free to connect with me on [my blog](https://www.paulsblog.dev), [Medium](https://medium.knulst.de/), [LinkedIn](https://www.linkedin.com/in/paulknulst/), [Twitter](https://twitter.com/paulknulst), and [GitHub](https://github.com/paulknulst).

* * *

**🙌 Support this content**

If you like this content, please consider [supporting me](https://www.paulsblog.dev/support/). You can share it on social media or [buy me a coffee](https://buymeacoffee.com/paulknulst)! Any support helps!

Furthermore, you can [sign up for my newsletter](https://www.paulsblog.dev/#/portal/signup/) to show your contribution to my content. See the [contribute page](https://www.paulsblog.dev/contribute/) for all (free or paid) ways to say thank you!

Thanks! 🥰

* * *

Photo by [frank mckenna](https://unsplash.com/@frankiefoto?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)