Caddy web server setup on a VPS

UPDATE 2022-03: caddy is now available from official Pacman repositories, I updated the setup script to use pacman instead of downloading Caddy manually from the website.

Caddy is a single-binary web server and reverse proxy with automatic HTTPS and simple config. This guide describes how to setup a Caddy instance on an Arch Linux VPS (but the process should be the same for other systemd-based Linux distributions).

VPS setup

Create a new VPS…

…or use an existing one, Caddy typically just needs free ports 80 and 443 (it can run on any other port too, acording to your config, but typically, you want these two ports for a reverse proxy). For this guide, I’m using Arch Linux on Linode, as it’s a rolling release with a reasonably small userspace.

Basic distro setup

Add your SSH key (on Linode, you can do this even before creating the VPS) and disable password authentication. If you want to deal with a firewall, this would be a good time to also set it up.

The setup script below will create a caddy user automatically and run Caddy under it, and set permissions to reasonable defaults. I’d recommend making sure that important data directories don’t have 777 access for everyone, to prevent any security issues in case Caddy is compromised (although that’s hopefully unlikely). Something like chmod -R go-rwx <dir> should do the trick.

Domain setup

Typically, if you want to use Caddy as a reverse proxy, you’ll need to setup the domains (add A and AAAA records for the desired domain, pointing to the VPS IP address). Then, enter the domains in the Caddyfile config during installation in the next step.

Caddy installation & setup

I wrote an automated script that should set everything up, including a new caddy user and a systemd service that automatically starts Caddy on boot and restarts it in case of a failure. Just upload it to the VPS, chmod +x and run it, it should do everything else automatically. During the installation, the config file /var/lib/caddy/Caddyfile will be opened in vim - enter your desired config (see examples below and in official Caddy docs), then save and exit with <escape>:wq, which will continue the setup.

In case you need multiple installs of Caddy for some reason, change install_name variable at the top of the script. This will affect the name of both the new user and the systemd service.

One surprising gotcha is that when serving static files, Caddy apparently needs at least read access to all parent directories of the served directory. If Caddy still doesn’t seem to work, check your DNS to ensure the domains are correctly setup to resolve to the VPS.

If something in the script itself doesn’t work for you, send me an e-mail at, hopefully I’ll find some time to figure it out.

Example Caddyfile config

Official docs:

Typically, you’d use Caddy as a reverse proxy, with some domains possibly served from a static webserver. The following config illustrates how to do that:

# proxy requests for to a service listening locally on port 10000 {
	reverse_proxy localhost:10000

# for requests to, serve files from the provided root directory {
	root * /path/to/www/dir/public_html