Multi-domain certificates managed automatically

For anybody needing to support multiple custom domains (those outside your control) and wildcard domains, you know the verification and management of certificates can be a pain.

Caddy server to the rescue:

I previously had apache setup as the reverse proxy to several different node applications.

In 15 minutes, I have installed a caddy server, turned off apache, and configured it to reverse proxy to the node applications. But the best part is that Caddy then completely and automatically provisions Let’s Encrypt certificates and manages the renewals. Customers can just point their domain to your app, and the Caddy server takes care of the domain validation, etc.

4 Likes

I’ve never used Caddy but I will take a look at it. In my case I’ve been an NGINX fan and for quick setup of reverse proxying and ssl I’ve used from time to time NGINX Proxy Manager.

Specially for my own self hosted applications but I could see it could work for simple projects.

Thanks for the Caddy tip.

Hi Ken,

May I know how you’re deploying the NodeJS apps? Is it through Docker? And do you configure Caddy to reverse proxy to the port published by the NodeJS app (127.0.0.1:1234) or do you use the container’s name (e.g.: web-1:3000)?

I’ve done this a couple ways. Currently I’m doing it all through the docker-compose inside the wappler target, but previously I was building Caddy outside of Docker. I did so for one main benefit, Caddy would stay operational during a deploy so I could display a custom maintenance page. I don’t remember why I moved to moving it inside of the wappler target, but I need to get back to my original method as the maintenance page is quite nice to have.

My current setup is as follows. I should note that there are some redundancies in this setup; nothing that causes any problems, just haven’t tidied things up from when I was learning.

Dockerfile for caddy. This actually has 2 plugins; The first is to enable the use of labels within docker-compose, and the second allows for wildcard domains from digital ocean (there are other plugins for other dns providers.)

ARG CADDY_VERSION=2.6.4
FROM caddy:${CADDY_VERSION}-builder AS builder

RUN xcaddy build \
    --with github.com/lucaslorentz/caddy-docker-proxy/v2 \
    --with github.com/caddy-dns/digitalocean

FROM caddy:${CADDY_VERSION}-alpine

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

CMD ["caddy", "docker-proxy"]

The target docker-compose has a caddy service added:

caddy:
    build:
      context: ./caddy-build
      dockerfile: Dockerfile
    ports:
      - 80:80
      - 443:443
    environment:
      - CADDY_INGRESS_NETWORKS=caddy
    networks:
      - caddy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - caddy_data:/data
    restart: 'unless-stopped'

and then the web service in docker-compose uses labels to configure Caddy.

There are 3 request matchers that all point to the upstream port of 3000 (the node app). This is where the redundancy becomes clear as https:// will trump the wild card domain.

The https:// matcher allows literally any domain to connect and get a certificate, so it has an “ask” process which authorizes the use or not. In my case if the domain presented is properly activated in the app, then when let it through, otherwise, the request gets blocked.

labels:
      caddy.email: "ken@uniqueideas.com"
      caddy.on_demand_tls.ask: 'https://mealproapp.io/api/1/4/caddy/on_demand_tls_ask'
      caddy.on_demand_tls.interval: '2m'
      caddy.on_demand_tls.burst: '5'
      caddy_0: 'https://'
      caddy_0.tls.on_demand: 
      caddy_0.reverse_proxy: '{{upstreams 3000}}'
      caddy_1: 'mealproapp.io'
      caddy_1.reverse_proxy: '{{upstreams 3000}}'
      caddy_2: '*.mealproapp.io'
      caddy_2.reverse_proxy: '{{upstreams 3000}}'
      caddy_2.tls.dns: 'digitalocean dop_v1_e39002REDACTED75ef8575a79c91f7f8c1'

Make sure to setup volumes and networks in the compose file as well to bring it all together.

networks:
  ...
  caddy:
    external: true

volumes:
  ...
  caddy_data: {}
1 Like