ERPNext.com Frappe Cloud Support Partners Foundation Frappe School

Letsencrypt with containerized Traefik for native frappe/bench

Recently I came across a very powerful bare metal server, with this powerful server I expect to install more apps than just frappe/erpnext.

Docker Swarm allows me to install wide range of apps on this server including frappe/erpnext.

Although I can install frappe/erpnext with docker swarm, I had a native bench installed on this server.

Once the bench is installed in production mode with dns multi-tenancy, it handles port 80, 443 and letsencrypt. With this default production setup I couldn’t fully use and manage Docker Swarm from portainer gui as expected.

In case of common docker swarm setup, Traefik becomes the entry point for all requests and runs on ports 80 and 443 published on host machine. Traefik also manages the domains and their Letsencrypt certificates.

Following setup assumes bench is running in production mode with port based multi-tenancy.
and NOT dns multi-tenancy.

  • Enable Port Base Multi Tenancy
  • Sites are running on separate ports on localhost. e.g: site1.local:4200, site2.local:4201
  • Ports are only accessed through localhost, no need for external access.
  • Setup Traefik in docker swarm mode (refer dockerswarm.rocks).

Admin can choose to add different stacks for each site or add sites in same stack.

Example add the following stack.yml with services representing sites.

version: "3.7"

services:
  site1-local:
    image: registry.gitlab.com/castlecraft/docker-craft/localizer:latest
    environment:
      - API_PORT=4200
    networks:
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.docker.network=traefik-public"
        - "traefik.enable=true"
        - "traefik.constraint-label=traefik-public"
        - "traefik.http.routers.erpnext-nginx.rule=Host(`site1.example.com`)"
        - "traefik.http.routers.erpnext-nginx.entrypoints=http"
        - "traefik.http.routers.erpnext-nginx.middlewares=https-redirect"
        - "traefik.http.middlewares.erpnext-nginx-https.headers.customrequestheaders.Host=site1.example.com"
        - "traefik.http.routers.erpnext-nginx-https.rule=Host(`site1.example.com`)"
        - "traefik.http.routers.erpnext-nginx-https.entrypoints=https"
        - "traefik.http.routers.erpnext-nginx-https.tls=true"
        - "traefik.http.routers.erpnext-nginx-https.tls.certresolver=le"
        - "traefik.http.services.erpnext-nginx.loadbalancer.server.port=8080"

  site2-local:
    image: registry.gitlab.com/castlecraft/docker-craft/localizer:latest
    environment:
      - API_PORT=4201
    networks:
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.docker.network=traefik-public"
        - "traefik.enable=true"
        - "traefik.constraint-label=traefik-public"
        - "traefik.http.routers.erpnext-nginx.rule=Host(`site2.example.com`)"
        - "traefik.http.routers.erpnext-nginx.entrypoints=http"
        - "traefik.http.routers.erpnext-nginx.middlewares=https-redirect"
        - "traefik.http.middlewares.erpnext-nginx-https.headers.customrequestheaders.Host=site2.example.com"
        - "traefik.http.routers.erpnext-nginx-https.rule=Host(`site2.example.com`)"
        - "traefik.http.routers.erpnext-nginx-https.entrypoints=https"
        - "traefik.http.routers.erpnext-nginx-https.tls=true"
        - "traefik.http.routers.erpnext-nginx-https.tls.certresolver=le"
        - "traefik.http.services.erpnext-nginx.loadbalancer.server.port=8080"

networks:
  traefik-public:
    external: true

Notes:

  • This exposes internal site1.local:4200 as site1.example.com via traefik running in docker swarm mode.
  • This setup works only on single machine. It will not work in cluster. We are running native bench on single machine.
  • registry.gitlab.com/castlecraft/docker-craft/localizer:latest image does the magic of exposing localhost API_PORT port as docker service on port 8080.

I had a similar problem but in Docker-Single Bench (not the Docker swarm).

I followed the official documentation of “Production Setup / Single Bench”.

I got the Containers up and running.

Then I wanted to run Portainer (with Letsencrypt SSL ) , I installed Certbot and portainer and brought portainer up using the commands :

$ docker volume create portainer_data`

$ docker run -d -p 4444 :9000 -p 8000:8000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer-data:/data \
-v /etc/letsencrypt/live/<my.domain.com>:/certs/live/ <my.domain.com>:ro
-v /etc/letsencrypt/archive/<my.domain.com>:/certs/archive/<my.domain.com>:ro \
portainer/portainer-ce --ssl --sslcert /certs/live/<my.domain.com>/cert.pem
–sslkey /certs/live/<my.domain.com>/privkey.pem

PS: I changed the port from 443 to 4444 as the container will not start with port 443 due the conflict with the already ruling https port.

Now, I have Portainer up, it can see the Stack, however I can’t edit it as it says:
" This stack was created outside of Portainer. Control over this stack is limited."

My Question is:
In your above mentioned Docker documentation, is it possible to add a part to install Traefik and Portainer first before installing the Frappe/erpnext stack (like the Docker swarm path), then create the so that admins can easily have a GUI to manage their containers?

Maybe a service definition for Traefik and Portainer then creating the frappe-bench-version stack from Portainer itself.

Thanks,

guide is from https://dockerswarm.rocks

I don’t think it is possible to start traefik and portainer from portainer itself.

Yes you’re right, you need to install Traefik and Portainer first then login to portainer and create the Stacks (Mariadb and Frappe/erpnext).

This the typical path in your Docker swarm documentation and it’s working fine.

But for Docker only, there is no documentation about how to integrate Portainer.

Do you believe that a single node Docker swarm deployment is almost equivalent (from required resources perspective) to the single bench Docker installation and I can simply use it instead of trying to implement Portainer to manage the frappe stack?

Thanks,

single bench setup is simple docker-compose, it is not docker-swarm setup.
It starts with docker-compose up instead of docker stack deploy ...

For a very cheap low resource setup, just use the docker-compose up, No extra apps that might slow down the vps. For future scalable setup use swarm.