Skip to content

Basic Usage of Caddy


So what is caddy. Well it is, at the most basic level, a web server like nginx or httpd. Like other web servers, caddy can serve content, or it can intercept web traffic to serve other sites (this is the core feature of a reverse proxy).

Unlike other web servers, caddy has some nice features that make it more palatable as a host:

  • Automatic SSL, whether you want to generate your own certs or use ACME servers
  • very simple syntax for writing our configs. Most sites require a single directive.
  • Based on go, a modern language with memory safety controls
  • Sane defaults. By default caddy sets up sites securely and (usually) without any additional configuration
  • A single, static binary. While our implementation will be docker focused, nothing is stopping you from installing caddy as a normal package or even as an executable on Windows.

Sounds good!

Looking at the Environment

We are using a Fedora 37 server running as a docker host with relevant data in /mnt/containers. We also have a web service (vaultwarden, but really it can be any web service) forwarded on port 8080.


vaultwarden is a bitwarden compatible password manager, designed to work automatically with bitwarden extensions.

    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: always
      - label:disable
      - 8080:80
      - ./container-data/data:/data
      - /etc/localtime:/etc/localtime:ro


This guide is representing the host using Visual Studio Code over SSH. You can read more on how that works here.

Awesome! Now how about our web service?

It’s working, but it’s being served on an IP and a port. Not ideal. It’s also not being served over HTTPS. Vaultwarden won’t even let you make an account without encryption (rightfully so):

Let’s fix that.

Routing by Hostname

Before we go any further, we need to stop using IP addresses and start using DNS. This allows us to leverage a reverse proxy to route based on the URL. The DNS hostname can be defined in:

  • Your current computer’s host file (no)
  • Your upstream router (sure)
  • Your Domain/DNS provider (sure if you want to get publicly valid certificates)

If you use OpenWRT (for example), you can do so in the latest version under hostnames.

  • I am going to point my upstream router to route to my lab docker IP.

Creating a Caddy Config

Before we start up Caddy, we need to create a config. In caddy terms this uses a syntax called a Caddyfile.


You can read up more on the caddyfile syntax here

Getting started is easy.

  • Create a folder called caddy, a subfolder called container-config, and a file called Caddyfile (note the capitalization).
  • Paste the following config
http://warden.<your-domain> {
    reverse_proxy http://<your-ip>:8080


If that looks like it’s almost too simple, that’s just Caddyfiles for you. Very easy syntax

Installing Caddy

righto, let’s get the caddy service started.

  • Place the following docker-compose.yaml in a caddy folder:


We are using the latest tag here for convenience. In production, you should pin the version of caddy you are using to prevent updates potentially introducing breaking changes.

    image: caddy:latest
    container_name: caddy
    restart: unless-stopped
      - label:disable
      - "80:80"
      - "443:443"
      - "443:443/udp"
      - ./container-config:/etc/caddy
      - /etc/localtime:/etc/localtime:ro


If you are wondering what label:disable does, it automatically sets SELinux folder permissions for volumes on a SELinux enabled distro, like fedora or centOS

  • Bring it up with docker-compose up -d

  • If all goes well, you can now access your web service (vaultwarden) on your URL!

Moving On

Alright, we’ve now managed to route a service by the URL (and potentially route many more services in the same fashion). However, we’re only halfway there. Let’s cover IP whitelisting, encryption, and leveraging docker networks in Advanced Usage of Caddy.