Image of Georg Kalus
Georg Kalus
/

n8n on an IONOS VPS

n8n on IONOS

After having set up n8n on a Scaleway instance, I want to try out the pre-configured n8n on an IONOS VPS (Virtual Private Server), which has a very low price of 3€/month for the smallest instance with 4 vCores and 8 GB RAM.

With the Scaleway instance, I took an easy but rather costly way to use a Scaleway Load Balancer for SSL termination. To get the full benefit of the low priced IONOS VPS, I want to make ends meet without additional resources.

My goal is simple: a n8n instance with a (sub-) domain name pointing to the n8n editor UI.

Set up a IONOS VPS for n8n

Let’s begin by creating a Virtual Private Server in IONOS. After ordering a VPS instance from the IONOS website (I chose the smallest VPS M), it is up and running just a few minutes after order confirmation. At first I was slightly confused when I realized that I had “just” received a plain VM running AlmaLinux. No sight of n8n. Two steps were necessary to get the IONOS pre-configured n8n:

  1. install the IONOS provided Ubuntu 24.04 + n8n image on the VM.
  2. change the firewall rule to open the n8n default port 5678.

Use pre-built Ubuntu + n8n image

IONOS provides an Ubuntu 24.04 + n8n image, which is a normal Ubuntu plus a compose file in the home folder to spin up n8n in a docker container. To install the image, sign in to the IONOS customer account, select the VPS instance and click on the Actions button to “Reinstall image”.

(by the way it seems IONOS are working on making the whole UI multi-language. At the moment, the VPS management parts of the UI seem to be German only)

Reinstall image in the IONOS user interface

I took the screenshot after I had done this step, which is why the OS in the screenshot is already Ubuntu 24.04 + n8n.

The Ubuntu 24.04 + n8n image provided by IONOS

Once the image is installed (you will get an email notification once it is ready), n8n is running on the VPS. To access the editor UI, we need to open the n8n default port in the firewall.

Change firewall rule to n8n port

By default, only the ports for SSH and http(s) are open. We can open a port by clicking on the rule.

Firewall rule for n8n port 5678 on IONOS

Once the port 5678 is added to the firewall rule, the n8n editor UI can be accessed under http://IP-OF-THE-IONOS-VPS:5678.

Two and a half things are not ideal yet:

  1. we are not using https yet
  2. we do not have a domain name yet
  3. the port is not so nice. It would be nicer to have the editor UI on 443.

We are solving all 3 points by putting a Traefik reverse proxy in front of n8n.

Use Traefik as reverse proxy for n8n with a Let’s Encrypt certificate

There is a step-by-step guide in the IONOS help center about how to extend the configuration to use a SSL certificate. While following the general idea of the guide, I want to use a Let’s Encrypt certificate instead of one that I am bringing.

My compose.yaml therefore contains a label traefik.http.routers.n8n.tls.certresolver=letsencrypt that points to a certificate resolver configured in the traefik.yaml configuration.

compose.yaml
services:
  traefik:
    image: "traefik:v2.11" # Fixed version for stability
    restart: always
    command:
      - "--configFile=/etc/traefik/traefik.yaml"
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:8080:8080"
    volumes:
      - traefik_data:/data # Traefik data
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Certificates
      - traefik-certs:/certs
      # Mount traefik config
      - ./traefik.yaml:/etc/traefik/traefik.yaml:ro

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
      # The router shall only listen to 'websecure' (HTTPS)
      - traefik.http.routers.n8n.entrypoints=websecure
      # The router shall use TLS
      - traefik.http.routers.n8n.tls=true
      # The router shall use letsencrypt as a resolver for the certificate
      - traefik.http.routers.n8n.tls.certresolver=letsencrypt

      # Definition of the Security-Header-Middleware (Name: n8n-secure-headers)
      - traefik.http.middlewares.n8n-secure-headers.headers.STSSeconds=315360000
      - traefik.http.middlewares.n8n-secure-headers.headers.browserXSSFilter=true
      - traefik.http.middlewares.n8n-secure-headers.headers.contentTypeNosniff=true
      - traefik.http.middlewares.n8n-secure-headers.headers.forceSTSHeader=true
      - traefik.http.middlewares.n8n-secure-headers.headers.SSLHost=${DOMAIN_NAME}
      - traefik.http.middlewares.n8n-secure-headers.headers.STSIncludeSubdomains=true
      - traefik.http.middlewares.n8n-secure-headers.headers.STSPreload=true

      # Assign Middleware to router
      - traefik.http.routers.n8n.middlewares=n8n-secure-headers@docker

    environment:
      # These environment variables are using the .env file
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_RUNNERS_ENABLED=true
      - NODE_ENV=production
      - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files

volumes:
  n8n_data:
  traefik_data:
  traefik-certs:

In the Traefik configuration I have mainly added the certificateResolvers section to configure the Let’s Encrypt certificate.

traefik.yaml
# Static Traefik configuration (traefik.yaml)
api:
  insecure: false # Setting this to true allows the Traefik dashboard (optional)
providers:
  docker:
    exposedByDefault: false # Only containers with 'traefik.enable=true' are considered
      
entryPoints:
  web:
    address: ":80"
    # automatic redirect from HTTP to HTTPS
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
  websecure:
    address: ":443"
    http:
      tls: {} # activate TLS for this endpoint

certificatesResolvers:
  letsencrypt:
    acme:
      email: your@email
      storage: /certs/acme.json
      caServer: https://acme-v02.api.letsencrypt.org/directory # prod (default)
      # caServer: https://acme-staging-v02.api.letsencrypt.org/directory # staging
      httpChallenge:
        entryPoint: web

The last piece of configuration is to add an environment file .env with the variables used in the compose file for the domain.

.env
# DOMAIN_NAME and SUBDOMAIN together determine where n8n will be reachable from
# The top level domain to serve from
DOMAIN_NAME=website.eu

# The subdomain to serve from
SUBDOMAIN=n8n

# The above example serves n8n at: https://n8n.website.eu

# Optional timezone to set which gets used by Cron and other scheduling nodes
GENERIC_TIMEZONE=Europe/Berlin

The final step is to configure a DNS record for the domain to point to the IP of our VPS.

Epilogue

After having put together this example and this guide, I found a second step-by-step guide in the IONOS help centre describing the exact same setup: n8n with Traefik and a Let’s encrypt certificate.

This setup of course works with any provider, not just IONOS. All we need is a server with a Docker runtime and a domain that we are controlling.

Happy automation with n8n for as cheap as 3€/month.

To top