Getting started with caddy web server
Osita Chibuike
Posted on May 26, 2018
The piece was originally published on my blog at legobox
Background
Last year I came across a server called caddy, I kinda liked it because it's name sounded similar to that of one of my favourite rap artiste cardi b, sounds weird right?, just kidding, that not why I loved the caddy web server, rather it was a much simpler server to install and configure than nginx, and what’s more it comes with its own integration of ssl certificates for your domains and these includes wildcards, in this article we are going to take a birds eye view of the server and get to understand why it's a great tool.
Prerequisite
There are really not much to know about, before diving into this, but it would be nice if you’ve got the following.
- At least a good understanding of servers and proxying.
- A general understanding of SSL - Secure Shell Layer and HTTPs
With these, you are good to go.
Origins.
Long story short caddy was created by Light Code Labs, and made open source, it operates in a similar manner to nginx with the use of server blocks, just that in this case all configurations can be done in one file called the Caddyfile, It implements https (yea emphasis on s) once its specified as part of the domain name.
Its light and lightening fast and has lots of really good documentation associated with it. Since I’ve been able to convince you (hopefully), let us have a look at how we can install and setup caddy.
Setting up caddy
First things first, you can download caddy by visiting the website (https://caddyserver.com/download), installation is pretty straightforward and easy to get on with, Most of it has been specified by their documentation, If you are looking to set up for a docker container, you might be interested in checking out this docker image.
If you downloaded the zip file, The follow these instructions to get rolling.
First, put the caddy binary in the system wide binary directory and give it appropriate ownership and permissions:
sudo cp /path/to/caddy /usr/local/bin
sudo chown root:root /usr/local/bin/caddy
sudo chmod 755 /usr/local/bin/caddy
Give the caddy binary the ability to bind to privileged ports (e.g. 80, 443) as a non-root user:
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/caddy
Set up the user, group, and directories that will be needed:
sudo groupadd -g 33 www-data
sudo useradd \
-g www-data --no-user-group \
--home-dir /var/www --no-create-home \
--shell /usr/sbin/nologin \
--system --uid 33 www-data
sudo mkdir /etc/caddy
sudo chown -R root:www-data /etc/caddy
sudo mkdir /etc/ssl/caddy
sudo chown -R root:www-data /etc/ssl/caddy
sudo chmod 0770 /etc/ssl/caddy
Place your caddy configuration in the appropriate directory and give it appropriate ownership and permissions:
sudo cp /path/to/Caddyfile /etc/caddy/
sudo chown www-data:www-data /etc/caddy/Caddyfile
sudo chmod 444 /etc/caddy/Caddyfile
Create the home directory for the server and give it appropriate ownership
and permissions:
sudo mkdir /var/www
sudo chown www-data:www-data /var/www
sudo chmod 555 /var/www
Let's assume you have the contents of your website in a directory called 'example.com'.
Put your website into place for it to be served by caddy:
sudo cp -R example.com /var/www/
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 555 /var/www/example.com
You'll need to explicitly configure caddy to serve the site from this location by adding
the following to your Caddyfile if you haven't already:
example.com {
root /var/www/example.com
...
}
Install the systemd service unit configuration file, reload the systemd daemon,
and start caddy:
wget https://raw.githubusercontent.com/mholt/caddy/master/dist/init/linux-systemd/caddy.service
sudo cp caddy.service /etc/systemd/system/
sudo chown root:root /etc/systemd/system/caddy.service
sudo chmod 644 /etc/systemd/system/caddy.service
sudo systemctl daemon-reload
sudo systemctl start caddy.service
Have the caddy service start automatically on boot if you like:
sudo systemctl enable caddy.service
If caddy doesn't seem to start properly you can view the log data to help figure out what the problem is:
journalctl --boot -u caddy.service
Use log stdout
and errors stderr
in your Caddyfile to fully utilize systemd journaling.
If your GNU/Linux distribution does not use journald with systemd then check any logfiles in /var/log
.
If you want to follow the latest logs from caddy you can do so like this:
journalctl -f -u caddy.service
You can make other certificates and private key files accessible to the www-data
user with the following command:
setfacl -m user:www-data:r-- /etc/ssl/private/my.key
The caddy file
While setting up you may have caught a glimpse of the caddyfile, now lets have a real look at it, how do we setup a server block, its as simple as this.
https://mydopesite.com {
root /path/to/site_files
}
Voila, that simple. Let's break it down so we can understand it some more (since its so difficult)
Site Address
The HTTP server uses site addresses for labels. Addresses are specified in the form scheme
://
host
:
port
/
path
, where all but one are optional.
The host portion is usually localhost or the domain name. The default port is 2015 (unless the site qualifies for automatic HTTPS, in which case it's changed to 443 for you). The scheme portion is another way to specify a port. Valid schemes are "http" or "https" which represent, respectively, ports 80 and 443. If both a scheme and port are specified, the port takes precedence. For example (this table assumes automatic HTTPS is applied where it qualifies):
:2015 # Host: (any), Port: 2015
localhost # Host: localhost; Port: 2015
localhost:8080 # Host: localhost; Port: 8080
example.com # Host: example.com; Ports: 80->443
http://example.com # Host: example.com; Port: 80
https://example.com # Host: example.com; Ports: 80->443
http://example.com:1234 # Host: example.com; Port: 1234
Wildcard characters *
can be used in a hostname. A wildcard must take the place of an entire domain label: *.example.com
is valid but foo*.example.com
is not.
Path Matching.
Some directives accept an argument that specifies a base path to match. A base path is a prefix. If the URL starts with the base path, it will be a match. For example, a base path of /foo
will match requests to /foo
, /foo.html
, /foobar
, and /foo/bar.html
.
Directives
Most directives invoke a layer of middleware. Middleware is a small layer in the application that handles HTTP requests and does one thing really well. Middleware are chained together (pre-compiled, if you will) at startup. Only middleware handlers which are invoked from the Caddyfile will be chained in, so small Caddyfiles are very fast and efficient.
The syntax of arguments varies from directive to directive. Some have required arguments, others don't. Consult the documentation of each directive for their signatures.
Placeholders
In some cases, directives will accept placeholders (replaceable values). These are words that are enclosed by curly braces { }
and interpreted by the HTTP server at request-time. For example, {query}
or {>Referer}
. Think of them like variables. These placeholders have no relationship to the environment variables you can use in Caddyfiles, except we borrowed the syntax for familiarity.
If you are looking to understand some more about this, it would be wonderful to invest some time into the docs. (click here to get there)
Proxying and Load balancing in caddy
Pulling off proxy actions in caddy is as simple as setting up the proxy directive.
This middleware adds a placeholder that can be used in log formats: {upstream} - the name of the upstream host to which the request was proxied.
https://mydomain.here{
// usual structure is (proxy from to)
proxy /api localhost:9005
}
The above proxy makes all /api routes redirect to localhost:9005 (the machine’s localhost).
You can use the policy options in the proxy to set up proxying algorithm based on the same concept if the nginx algorithms.
proxy / web1.local:80 web2.local:90 web3.local:100 {
policy round_robin
}
Here are the policies associated with load balancing
Policies
There are several load balancing policies available:
- random (default) - Randomly select a backend
- least_conn - Select backend with the fewest active connections
- round_robin - Select backend in round-robin fashion
- first - Select the first available backend in the order they are defined in the Caddyfile
- ip_hash - Select backend by hashing the request IP, distributing evenly over the hash space based on the total number of backends
- uri_hash - Select backend by hashing the request URI, distributing evenly over the hash space based on the total number of backends
- header - Select by hashing the value of a given header, specified by the [value] after the policy name, distributing evenly over the hash space based on the total number of backends
Conclusion
It’s clear by now that I do love working with caddy, it's a simple tool that does the job so well, there are many options for web servers out there, but the benefits this server gives is quite exciting and worthwhile.
If you are looking to learn some more about it, feel free to check out the project on https://caddyserver.com.
Posted on May 26, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.