A single node server is perfectly suited for prototyping. It is quick to set up, requires little maintenance and still allows the first rollout of the application.


If you are tired of reading clone my Github repo und follow the instructions in the main as well as in the ./01_server/!

Renting a server

Nowadays it is quite easy to get the appropriate hardware. Thanks to virtualization, cloud providers can deliver the desired resources in seconds.

I’ve personally hosted my virtual private server (vps) at Contabo and am very satisfied with its support. I’m paying 5€ per month for an ubuntu server with 4 CPU cores, 8GB of RAM and 200GB of SSD. My domain additionaly is 1€ per month.

Contabo’s website is in German, but thanks to google translator this should not be a problem for you:

  1. Select your desired product an configure it: (you will have to pay the first year in advance)
  2. Once they receive the money, they will send you an e-mail including the login-data
  3. Log-in at and change the password for the server
  4. Via that portal you can also order your domains and administrate the DNS-Zones
  5. Connect to your server as described here:

General server setup

Let’s add an user and change to its dirictory:

useradd -m -s /bin/bash <USER>
cd /home/<USER>/

Make sure net-tools and apache2-utils is installed and security updates are being installed continiously:

apt-get update
apt-get upgrade
apt-get install

apt install net-tools
apt install apache2-utils -y
apt-get install -y unattended-upgrades

Install docker and docker-compose

Installing Docker

Docker is a container engine that allows you to run your applications in protected environments.

# For adding PPA (Personal Package Archive) - needed for docker installation
apt-get install -y software-properties-common

# Docker
sudo apt remove --yes docker docker-engine \
    && sudo apt update \
    && sudo apt --yes --no-install-recommends install \
        apt-transport-https \
        ca-certificates \
    && wget --quiet --output-document=- \
        | sudo apt-key add - \
    && sudo add-apt-repository \
        "deb [arch=$(dpkg --print-architecture)] \
        $(lsb_release --codename --short) \
        stable" \
    && sudo apt update \
    && sudo apt --yes --no-install-recommends install docker-ce \
    && sudo usermod --append --groups docker "$USER" \
    && sudo systemctl enable docker \
    && printf '\nDocker installed successfully\n\n'

Install docker-compose

Docker-compose is a command line program for container orchestration and perfectly suited for single nodes (only one server).

sudo wget \
        --output-document=/usr/local/bin/docker-compose \ \
    && sudo chmod +x /usr/local/bin/docker-compose \
    && sudo wget \
        --output-document=/etc/bash_completion.d/docker-compose \
        "$(docker-compose version --short)/contrib/completion/bash/docker-compose" 

Advanced settings

A friend of mine once wrote a test application that continiously printed “hello world” to the command line. On the next day the whole server was down. What happend?

The output of the container flooded the disc and no other application was able to do its IO operations.

To avoid that inscidence append the docker deamon.json via following command:

printf "{\n\"log-driver\": \"json-file\",\n\"log-opts\": {\n \"max-size\": \"10m\",\n \"max-file\": \"2\"\n }\n}"  > /etc/docker/daemon.json
systemctl reload docker

Treafik as reverse proxy

I use traefik to simplify dynamic routing on my server.



Traefik handels the incoming requests to my server. It
– routes them to the desired services,
– redirects http to https and
– secures some of my services via BasicAuth.

This is not a traefik introduction. So please refere additional information on traefik’s website.

NOTE: There had been breaking changes from v1 to v2! Keep that in mind when searching for information on the internet!


For all the server stuff I created a folder in the user directory:

mkdir /home/<USER>/01_server
cd /home/<USER>/01_server

Thanks to docker, the rest of the installation is very simple:

Store your environment variables in a .env file

echo "DOMAIN=<YOUR_DOMAIN>" > .env
echo "EMAIL=<YOUR_MAIL>" >> .env
echo "COMPOSE_PROJECT_NAME=server" >> .env
echo "TRAEFIK_CREDS=$(echo $(htpasswd -nb <USER> '<PASSWORD>'))" >> .env

Write your traefik.yml

This file contains your traefik settings. Create it with the editor of your choice. For example nano traefik.yml. You can exit the editor with CTRL+x and save the changes with y+ENTER.

api: {}

  level: INFO

  docker: {}

    address: ":80"
    address: ":443"

      email: ${EMAIL}
      storage: ./acme.json
        entryPoint: http
      tlsChallenge: {}

Create the acme.json file

Traefik stores the https-certificates here.

touch acme.json
chmod 600 acme.json

Write your docker-compose.yml

version: '3.7'


    image: traefik:latest
    container_name: traefik
      - --configFile=/traefik.yml
      - 80:80
      - 443:443
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./acme.json:/acme.json"
      - "./traefik.yml:/traefik.yml"
      - "/etc/timezone:/etc/timezone:ro"
      - "/etc/localtime:/etc/localtime:ro"
      # Dashboard
      - traefik.http.routers.traefikRouter.rule=Host("traefik.${DOMAIN}")
      - traefik.http.routers.traefikRouter.service=api@internal
      - traefik.http.routers.traefikRouter.middlewares=traefikAuth
      - traefik.http.routers.traefikRouter.tls=true
      - traefik.http.routers.traefikRouter.tls.certresolver=mytlschallenge
      - traefik.http.middlewares.traefikAuth.basicauth.users=${TRAEFIK_CREDS}
      # Config
      - traefik.http.middlewares.sslredirect.redirectscheme.scheme=https
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=http
      - traefik.http.routers.http-catchall.middlewares=sslredirect      
    restart: always
      - proxy

    external: true

DONE! Start the service!

docker-compose up -d

Congratulations 🎉 You have your reverse-proxy up and running! Visit the secured dashboard at traefik.<DOMAIN>!

And now? In the following section you will see how easy it is to bring up new services!

Monitoring the server with Netdata

Netdata Dashboard

Let’s install netdata and publish it via the domain netdata.<DOMAIN>.


Generate a BasicAuth user and password for netdata:

echo "NETDATA_CREDS=$(echo $(htpasswd -nb <USER> '<PASSWORD>'))" >> .env

And look up the process group id of docker:

grep docker /etc/group | cut -d ':' -f 3

Write the netdata-compose.yml

version: '3.7'


    image: netdata/netdata
    container_name: netdata
      - 19999
      - PGID=998 # grep docker /etc/group | cut -d ':' -f 3
      - SYS_PTRACE
      - apparmor:unconfined
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/passwd:/host/etc/passwd:ro
      - /etc/group:/host/etc/group:ro
      - traefik.http.routers.netdataRouter.rule=Host("netdata.${DOMAIN}")
      - traefik.http.routers.netdataRouter.entrypoints=https
      - traefik.http.routers.netdataRouter.tls=true
      - traefik.http.routers.netdataRouter.tls.certresolver=mytlschallenge
      - traefik.http.routers.netdataRouter.middlewares=netdataAuth
      - traefik.http.middlewares.netdataAuth.basicauth.users=${NETDATA_CREDS}
      - proxy

    external: true

Change the PGID number and start the service with:

docker-compose up -d -f netdata-compose.yml

docker-compose looks automaticly after the docker-compose.yml file. If you rename it, you have to tell docker-compose the filename via the -f flag. Alternativly you can write both services in one file, as I did here.

That’s it! 🎊 We’re done for today!

One thought on “#01 Setting up a single node docker server

  1. Royal CBD says:

    We’re a group of volunteers and starting a new scheme in our community.
    Your website provided us with valuable information to work on. You’ve done an impressive job and
    our entire community will be thankful to you.

Leave a Reply

Your email address will not be published. Required fields are marked *