Unlocking Secure Access: Docker, Tailscale, and Caddy for HTTPS Services

Securely Exposing Docker Services with Tailscale and Caddy: An HTTPS Guide

version: "3.7"

networks:
  # network created via docker cmd line, 
  # and all other containers are also on it
  proxy-network:
    name: proxy-network

services:
  caddy:
    image: caddy:latest
    restart: unless-stopped
    container_name: caddy
    hostname: caddy
    networks:
      # caddy is in the network with the other containers
      - proxy-network
    depends_on:
      # wait for tailscale to boot
      # to communicate to it using the tailscaled.sock
      - tailscale 
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - /home/io/docker_config/caddy/Caddyfile:/etc/caddy/Caddyfile
      - /home/io/docker_config/caddy/data:/data
      - /home/io/docker_config/caddy/config:/config
       # tailscale creates its socket on /tmp, so we'll kidnap from there to expose to caddy
      - /home/io/docker_config/tailscale/tmp/tailscaled.sock:/var/run/tailscale/tailscaled.sock

  tailscale:
    container_name: tailscaled
    image: tailscale/tailscale
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - /dev/net/tun:/dev/net/tun
      - /home/io/docker_config/tailscale/varlib:/var/lib
      # https://github.com/tailscale/tailscale/issues/6849
      # add volume for the tailscaled.sock to be present on the host system
      # that's where caddy goes to communicate with tailscale
      - /home/io/docker_config/tailscale/tmp:/tmp
    environment:
      # https://github.com/tailscale/tailscale/issues/4913#issuecomment-1186402307
      # we have to tell the container to put the state in the same folder
      # that way the state is saved on the host and survives reboot of the container
      - TS_STATE_DIR=/var/lib/tailscale
      # this have to be used only on the first time
      # after that, the state is saved in /var/lib/tailscale and the next line can be commented out 
      - TS_AUTH_KEY= < your generated key >

Caddyfile

(network_paths) {
  handle_path /backup/* {
    reverse_proxy /* syncthing:8384 <<<< those are my container names
  }
  handle_path /docker/* {
    reverse_proxy /* portainer:9000 <<<< those are my container names
  }
  reverse_proxy /* homer:8080 <<<< those are my container names
}

<machine-name>.<tailnet-name>.ts.net {
  import network_paths
}

http://192.168.2.30 {
  import network_paths
}

and don“t forget to generate the cert on it by running:

docker exec tailscaled tailscale --socket /tmp/tailscaled.sock cert <the server domain name>