A Guide to Using the Caddy Server on Linux

Caddy is a web server that’s extremely easy to configure and use. It provides HTTP/2 support, automatic SSL configuration with Let’s Encrypt, and an intuitive configuration syntax. It is cross-platform, and can run across all major operating systems, such as Linux, macOS and Windows.

In this article, we’re going to install Caddy on a Linux system and configure it to serve a website. These instructions should work on most popular distributions, and they were tested on Ubuntu 18.04/20.04, Debian 9/10, and CentOS 7/8.

Contents

Prerequisites

First of all, we will assume that you have root access on your system through sudo. However, if you can log in to the system directly as the root user, then you can simply remove the sudo from the beginning of the command before executing it.

If you are logged in as a non-root user, but do not have root access through sudo, then you can use the su command to obtain a root shell. Then, you can remove the sudo from the beginning of the commands, as mentioned earlier.

In addition, before you follow this article to install Caddy, there are a few packages that you need to install — nss-tools, setcap, wget and tar.

On Debian/Ubuntu, use the following command to install these packages:

sudo apt update
sudo apt install libcap2-bin libnss3-tools wget tar

On CentOS, use the following command to install these packages:

sudo yum install libcap nss-tools wget tar

Installing the Caddy binary

Head over to the Caddy releases page, which offers binaries for a variety of process architectures. To determine the processor architecture for your system, run the following command:

uname -m

Most often, servers and desktop systems run on Intel 64-bit processors. The output of the above command on such systems would be x86_64, which is also known as “amd64”. Therefore, you should download the .tar.gz file for Linux amd64:

wget https://github.com/caddyserver/caddy/releases/download/v2.0.0/caddy_2.0.0_linux_amd64.tar.gz

ARM based systems such as the Raspberry Pi, the output of the uname -m command is armv6l or armv7l. You can also find .tar.gz files for both these architectures on the releases page, and download them as shown above.

Once you have downloaded the file, extract the caddy binary to the /usr/local/bin directory by running:

sudo tar -C /usr/local/bin/ -xf caddy_2.0.0_linux_amd64.tar.gz caddy

To confirm that the binary was installed, run the following command:

caddy

You should see Caddy’s help text, as shown in the screenshot below. If you see this text, this would indicate that the binary was installed successfully.

Testing the "caddy" command.



Setting up user accounts and directories

Web servers such as Caddy are exposed to the internet. It is a best practice to run any internet facing software as a restricted user.

Therefore, we’ll set up a restricted user named caddy with the home directory of /etc/caddy by running the following command:

sudo useradd --shell /bin/false --home-dir /etc/caddy --system caddy

Under the home directory, Caddy stores configuration under the  .config directory, and certificates under the .local directory. So, run the following command to set up these two directories:

sudo mkdir -p /etc/caddy/.config /etc/caddy/.local

We’ll also need a directory in which we can tell Caddy to store access logs. For this purpose, we’ll create a directory /var/log/caddy with the following command:

sudo mkdir -p /var/log/caddy

Next, change the ownership of these directories to the caddy user:

sudo chown -R caddy: /etc/caddy /var/log/caddy

In addition, when you run software as a non-root user, Linux prohibits these processes from listening on to port numbers which are less than 1024. To bypass this restriction and run Caddy securely as a non-root user, add the cap_net_bind_service capability to the caddy binary:

sudo setcap cap_net_bind_service+ep /usr/local/bin/caddy

Auto-starting Caddy

Now that we’ve created the user and directories, we’ll set up Caddy as a system service. By doing this step, the Caddy web server will automatically start when you boot (or reboot) your system. You’ll also be able to start, stop, or force Caddy to reload its configuration by using the systemctl command. We’ll also define the working directory for Caddy through this file; which will set the default directory where Caddy will look for files to serve.

First, run the following command to determine the systemd version:

systemctl --version

This will list the version of systemd, as shown below:

Obtaining the systemd version

Note down the systemd version, since you need to create a different file based on the systemd version.

Next, use your favourite text editor to create the file /etc/systemd/system/caddy.service. Most distributions have the vi editor installed, so you can run:

sudo vi /etc/systemd/system/caddy.service

In the vi editor, press i to enter the editing mode.

If the systemd version is greater than 229, paste or type in the following content into the editor:

[Unit]
Description=Caddy web server
After=network-online.target

[Service]
User=caddy
Group=caddy
Type=exec
WorkingDirectory=/var/caddy/html

ExecStart=/usr/local/bin/caddy run -config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload -config /etc/caddy/Caddyfile
ExecStop=/usr/local/bin/caddy stop

LimitNOFILE=1048576
LimitNPROC=512

PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/etc/caddy/.local /etc/caddy/.config /var/log

CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

However, if the systemd version is less than 229, paste or type in the contents shown below. Since systemd versions less than 229 do not have the feature of adding security restrictions to the process at runtime, we’ve omitted those here.



[Unit]
Description=Caddy web server
After=network-online.target

[Service]
User=caddy
Group=caddy
Type=simple
WorkingDirectory=/var/caddy/html

ExecStart=/usr/local/bin/caddy run -config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload -config /etc/caddy/Caddyfile
ExecStop=/usr/local/bin/caddy stop

LimitNOFILE=1048576
LimitNPROC=512

[Install]
WantedBy=multi-user.target

Exit the editing mode by pressing “Esc”, and then type :wq to save the file.

Configuring Caddy to serve a website

The final step in configuring Caddy is to set up a Caddyfile, which tells Caddy about what to do when a HTTP request arrives. To create the Caddyfile, use:

sudo vi /etc/caddy/Caddyfile

Now, enter the editing mode by pressing “i”, and then enter the following content:

http:// {
    encode gzip
    file_server
}

This will instruct Caddy to serve files present in its current working directory, and compress HTTP response bodies with the gzip algorithm. Now, what’s the working directory for Caddy? As we mentioned previously, we have set up the working directory when we created the service file, as you can see in the line WorkingDirectory=/var/caddy/html.

Now, after typing in the above contents in the Caddyfile, press “Esc” to exit the editing mode, and then type :wq to save it.

Then, we will create the /var/caddy/html directory and put an index.html file inside it. Run the following commands to do so:

sudo mkdir -p /var/caddy/html
sudo bash -c 'echo "<h1>Hello from Caddy web server</h1>" > /var/caddy/html/index.html'

After making these changes, run the following command to start Caddy:

sudo systemctl start caddy

Now, visit the URL http://<server ip address>/, replacing the <server ip address> with the server’s public IP address. You should see a page like this:

Hosting a basic web page using Caddy

If you can see this web page, this means that your setup is working as expected. However, if you receive a connection error or a timeout when connecting to your website, it could indicate the server firewall is blocking traffic, or that Caddy was not configured properly.

To troubleshoot any Caddy configuration issues, you can run the following command to view errors:

sudo systemctl status caddy

Finally, you would also want to set up Caddy to automatically start when your system is restarted. Use this command to set the Caddy service for automatic reboots:

sudo systemctl enable caddy

Setting up a website with HTTPS

Now that you have a website up and running, you can configure Caddy to automatically add HTTPS to it.

To follow the steps in this section, you should have your own domain name. First, go to the DNS settings of your domain’s DNS provider. Create a subdomain, or edit the apex domain’s DNS entry so that it points to the IP address of the server.

After you have made the changes in DNS, edit the file /etc/caddy/Caddyfile so that the configuration looks like this instead:

{
    email <YOUR EMAIL ADDRESS HERE>
}

<YOUR DOMAIN NAME HERE> {
    encode gzip
    file_server
}

Make sure to replace the placeholders <YOUR EMAIL ADDRESS HERE> and <YOUR DOMAIN NAME HERE> with your email address and domain name respectively. There’s no need to specify the protocol “https://” when specifying the domain name. When you don’t specify a domain name, Caddy automatically obtains certificates. Then, it applies HTTPS to your website, and also sets up redirection from HTTP to HTTPS.

After making the above changes, reload the Caddy configuration by running:

sudo systemctl reload caddy

Wait for a few moments, and then run:

sudo systemctl status caddy

If all went well, the “status” command should show output stating that it’s obtained the certificates:

Caddy obtaining a SSL certificate

Now, using a browser, visit your domain name. The site will appear as HTTPS-protected, as shown below:

Hosting a HTTPS website with Caddy

Hosting multiple websites (virtual hosting)

Often, you want to serve requests for multiple domains from the same server. This concept is sometimes known as “virtual hosting“. You can add additional blocks to your Caddyfile to perform virtual hosting.

As an example you have three domains, example1.com, example2.com and example3.com, where each domain would serve different content to the user. The first step is to create directories for these domains, such as /var/caddy/example1.com, /var/caddy/example2.com and /var/caddy/example3.com, and store the required files in these directories.

Once you have the content in place, edit your Caddyfile like so:

{
    email <YOUR EMAIL ADDRESS HERE>
}

example1.com {
    root * /var/caddy/example1.com
    encode gzip
    file_server
}

example2.com {
    root * /var/caddy/example2.com
    encode gzip
    file_server
}

example3.com {
    root * /var/caddy/example3.com
    encode gzip
    file_server
}

After making the above changes, reload the Caddy configuration by running:

sudo systemctl reload caddy

Caddy will set up HTTPS on these three domains and serve the content from the respective directories.

If, for some reason, you want that the sites should be HTTP-only, you can add a http:// before each site’s name, like so:

http://example1.com {
    # Caddyfile directives for example1.com
}

Logging requests

By default, Caddy does not log requests. However, you can configure Caddy to log details through the “log” directive. By default, Caddy stores its logs as lines of JSON, although other formats are available too.

As we’ve seen in the previous sections, the Caddyfile consists of sections in which you add the configuration for every website. To configure logging, edit the Caddyfile so that every block looks like this:

<YOUR DOMAIN NAME HERE, OR SPECIFY "http://"> {
    encode gzip
    file_server
    log {
        output file /var/log/caddy/access.log
    }
}

Once you’ve saved the Caddyfile, reload the configuration with:

sudo systemctl reload caddy

Now, visit the website in a browser, in order to generate a few log entries. Once you’ve done that, run the following command to view the logs:

sudo cat /var/log/caddy/access.log

Here’s an example of how Caddy’s logs look like:

Logging requests with Caddy.

You can also configure “log rotation” with Caddy. When the size of your logs exceeds a certain threshold, Caddy moves the old log entries in a new file, and this is known as “rotation”. If the rotated log files stay there for extended period of time (that you specify), Caddy deletes the rotated logs.

By default, Caddy rotates log files when they reach 100 MB. The rotated log files are deleted after 90 days, or when there are more than 10 rotated logs. If you want to customize these parameters, you can do it like so:

log {
    output file /var/log/caddy/access.log {
        roll_size 10MB
        roll_keep 5
        roll_keep_for 240h
    }
}

In this example, the logs are rotated after they exceed the 10MB limit. These logs are deleted after 10 days (240 hours), or when there are more than 5 rotated logs.

You can also specify different log files for different websites, as shown below:

example1.com {
    # other Caddyfile directives
    log {
        output file /var/log/caddy/example1.log
    }
}

example1.com {
    # other Caddyfile directives
    log {
        output file /var/log/caddy/example1.log
    }
}

Conclusion

In this article, we took a brief overview of Caddy’s functionality and how to configure and use it on a Linux system. If you want to learn more, you should begin by reading about the Caddyfile.

A real website will also often use dynamic content in addition to static content. If you have a PHP based website, you should take a look at the “php_fastcgi” directive. On the other hand, if you have a web app written in a language like Go, NodeJS or Rust that starts up its own HTTP server, use the “proxy” directive.

If you liked this post, please share it :)

You may also like...