Setting up Caddy on Rocky Linux 9

Some quick ’n dirty field notes, adapted from setting up the server that hosts this website.

I recently decided to move clouding providers. The reasons are unimportant.

This blog post is going to go over setting up the Caddy web server, on a Rocky Linux 9 virtual machine.

What is not in this guide

When I set this up, I used:

  • Terraform for automating the infrastructure setup;
  • and Ansible for automating the infrastructure configuration.

I will not be including any of my Terraform .tf files, or links to my Ansible roles and playbooks, in this guide.

Instead, I will provide you the commands that you can run manually. This allows you to automate them however you desire.

Get a virtual machine

For this guide, I used Linode. You can use whatever cloud provider (or local hypervisor) you prefer. If you are doing this as a bare-metal install, that is fine too!

There are some important things to note about the Linode-provided virtual machine I set up:

  • SELinux was enabled and set to “enforcing”, by default;
  • firewalld was enabled by default.

These are important to note, because not every cloud provider provides Rocky Linux virtual machines with these enabled. On DigitalOcean for example, SELinux was enforcing, but firewalld was not enabled.

The rest of this guide assumes that SELinux is enforcing, and that firewalld is enabled as well. There are too many guides out there where the second or third step is disabling SELinux. This kind of stuff does not fly in the real world, so leave SELinux alone, and let it enforce stuff.

DNS

Once you have the virtual machine created, it is at this point I recommend creating any required DNS records for pointing your desired domain name, to your virtual machine.

Installing Caddy

dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy

Before you start and enable caddy.service, you should configure it.

Configuring Caddy

The caddy package puts a bare-bones configuration file at /etc/caddy/Caddyfile. Edit this file to look something like this:

{
  debug
}

mysite.com {
  encode zstd gzip
  root * /srv/mysite.com/
  file_server browse {
    index index.html
  }

  log {
    output file /var/log/http/mysite.com.access.log
    format json
    level ERROR
  }
}

Remember how I said Caddy has a lot of sane defaults and an simple configuration file? This is what I am talking about. The mysite.com block has no details about fetching TLS certificates, or performing HTTP-to-HTTPS redirection. Caddy handles that out of the box for you. You can configure how it retrieves TLS certificates, and handles HTTP-to-HTTPS redirection, but that is an exercise for you, dear reader.

The important thing to note from this Caddyfile sample, is

mysite.com {
  root * /srv/mysite.com/
  ...

and even then, just the root * /srv/mysite.com/ line. That configuration “directive” sets the directory that content for mysite.com will be served from.

Next, let’s create that directory.

Creating the content directory

mkdir -p /srv/mysite.com

Managing SELinux file context mappings

Even though SELinux is enabled and set to enforce on the standard Rocky Linux image from Linode, I still had to install an extra package to get the semanage(8) tool.

dnf install policycoreutils-python-utils

Next, we will need to manage the SELinux file context mappings. To do this, we will use semanage(8):

semanage fcontext -a -t httpd_sys_content_t /srv/mysite.com

This command says:

  • We want to manage the fcontext (file context)
  • By adding -a
  • The httpd_sys_content_t type
  • To the /srv/mysite.com directory.

Lastly, we want to use the restorecon(8) command to restore the SELinux security contexts to our content directory:

restorecon -rv /srv/nesv.ca

Upload your content

Personally, I do this with rsync(1).

First, I had to install the rsync package on the remote host:

dnf install rsync

For rsync(1) to work, both your local host, and the remote host you are uploading your content to, must have rsync installed.

Then back on your local machine, push the files to the content directory:

rsync -vz public/ user@remotehost:/srv/mysite.com/

Allowing traffic

Again, as a detail about this Rocky Linux VM provided by Linode, firewalld(1) is enabled as well. If we tried to start Caddy and view our website, we would get an error in the web browser saying something about the connection being interrupted.

Punching a hole in the firewall to allow HTTP and HTTP traffic is simple enough:

firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --add-service=https
  • The --permanent flag persists these changes so we do not have to run these commands again if the server is ever rebooted;
  • --zone=public says to issue this command against the public zone (read up on firewalld to learn about zones);
  • --add-service=http and --add-service=https is the important flag here, this adds the HTTP and HTTPS services to the firewall rules, effectively allowing traffic in on ports 80 (HTTP) and 443 (HTTPS).

Starting Caddy

Finally, start and enable the systemd caddy.service unit:

systemctl enable --now caddy.service

Postamble

Fantastic! At this point, if you pop open your web browser and go to your domain, you should be greeted by your snazzy website, being hosted by Caddy!

Hopefully, you remembered to create a DNS record to point your domain at the virtual machine’s IP address.