Brief overview of this server

Hi! My third attempt on this server setup. Long nights spend figuring this whole programming environment out, but I guess for now I found a decent setup to keep my personal cloud growing.

Brief overview of this server

Hi! My third attempt on this server setup. Long nights spend figuring this whole programming environment out, but I guess for now I found a decent setup to keep my personal cloud growing. You can find the repository here.

I stumbled upon many technologies on my way to here and gained some knowledge. Let me see if I can get some posts out of it.

Maybe someone may have guessed that this blog is presented to you by ghost, a full featured, yet simple blog CMS, with post creation, publishing, drafts, social media, custom themes and all that jazz.

Please visit https://andreaskrahl.de/, where you will find the default page which as of now shows the different gateways to the applications.

https://andreaskrahl.de/ is an angular application neatly packed into a small docker image. https://www.andreaskrahl.de/ is forwarded to the same application. A small docker nginx webserver serves static html.

The issue

I was looking for a reverse-proxy in a docker environment, which serves my applications (usually served on some port) independently based on the subdomain, like blog. and test.

Not that hard of an issue, right? I found this article on freecodecamp, which gives you an easy to follow setup guide.

But I want it automated? Now for that I found two options. My first choice was jwilder/nginx-proxy, which was working great. I ran this setup for a while.

But wait now I need certificates... And I must create them all by manually...

After digging into certificate creation and distribution I accidentally stumbled over this guide. Neat! That is my exact issue with trusted dynamic lets encrypt certificate creation. Fortunately (or unfortunately) the guide is in German (you could work it out with the code snippets, try the setup yourself!).

Great! Now the server serves all my subdomains appropriately, all I need to do now is to create a docker-compose.yml and fill it with an image, some properties and the required environment variables of the jwilder/nginx-proxy setup. The server served its purpose as a playground and a traveling blog I created.

Now here comes the part which the project managers and maybe customers do not like. I was still not happy with this setup...

It felt a little clunky to get it up and running. Deployments and maintenance were also not running smoothly.

Now here I am. Digging deeper and deeper… starting with 'how do I program my own proxy server' going to starting to learn the language rust. Now I want to manage the docker container to automate cert creation and listen on their ports...Wait docker is written in Go? What is Go? What are actually container? containerd? runc? I went down a deep rabbit hole with little sleep on that one, up to the point where linux virtual containers are created and also why it is so difficult to make container in general for both windows and unix possible.

My second option is traefik, which I already stumbled upon along the way. At the time of writing debugging and getting the configuration right was kind of a struggle, because in their version 1.x to 2.x are several breaking changes and googling this stuff will get kind of confusing. But once I handled the configuration of this setup, it went out pretty slim and just works, which is awesome!

Let's see how this setup will work from here on, eventually I will get bored and I will again don't like this setup and write and awesome container runtime in rust.

My full traefik docker-compose.yml looks like the following.

# v1.0.0
version: "3"
# See https://docs.docker.com/compose/overview/ for more information.

# If you make changes to this file or any related files, apply them by
# navigating to the directory that holds this file and run this as root:
#   docker-compose down; docker-compose up -d

networks:
  webgateway:
    driver: bridge

volumes:
  vol-db:

services:
  traefik:
    # https://hub.docker.com/_/traefik/
    image: traefik:2.4
    container_name: traefik
    restart: unless-stopped
    networks:
      - webgateway
    command:
      - "--log.level=${LOG_LEVEL}"
      - "--providers.docker.exposedByDefault=true"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.web.http.redirections.entryPoint.scheme=https"
      - "--entryPoints.web.http.redirections.entryPoint.to=websecure"
      - "--entryPoints.websecure.address=:443"
      - "--certificatesResolvers.myresolver.acme.email=${ACME_EMAIL}"
      # enable if stagign certificate creation, otherwise default prod is taken
      # - "--certificatesResolvers.myresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesResolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--certificatesResolvers.myresolver.acme.tlsChallenge"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`${DOMAIN_DASHBOARD}`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=myresolver"
      - "traefik.http.routers.dashboard.tls=true"
      - "traefik.http.services.dashboard.loadbalancer.server.port=8080"
    # Enable the next line to enable a basic auth login prompt for the dashboard.
    #- "traefik.frontend.auth.basic=${BASIC_AUTH}"

    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      # Since we cannot place environment variables in toml file, production file uses commands instead
      #   - "./traefik.toml:/traefik.toml"
      - "vol-db:/letsencrypt"

  whoami:
    image: "containous/whoami"
    container_name: "test"
    networks:
      - "webgateway"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`${DOMAIN_TEST}`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls=true"
      - "traefik.http.routers.whoami.tls.certresolver=myresolver"

If you stayed up until here, awesome! Thank you for reading and happy coding.