How to Host a Website with the Caddy Web Server on Linux

Caddy is a new web server that’s extremely easy to configure and use. It provides HTTP/2 support, built-in integration with Let’s Encrypt and an intuitive configuration syntax. In addition, you can add various plugins for additional features, such as ratelimiting, blocking IPs, and minifying assets.

In this article, we’re going to install Caddy on a Linux system and configure it to serve a website. As with most of our guides here, these instructions should work on most popular distributions such as Debian, Ubuntu and CentOS.

Update (25th September 2017): At the time when this article was written, Caddy was free to use for any purpose. However, due to a recent change, you must buy a commercial license to use the official Caddy binaries for business use. In this article, we will show how to download the free version, that can be used for personal and academic use.

Keep in mind that Caddy itself is still open-source, so you can compile Caddy on your own for business use without having to pay. The restriction only applies to the pre-built binaries from caddyserver.com.

Installing the Caddy binary

The Caddy project provides a shell script so that you can easily install Caddy on your system. First, you should download the script and make it executable.

wget https://getcaddy.com -O getcaddy
chmod +x getcaddy

Then, you simply need to execute the script like so:

sudo ./getcaddy personal

If you want to get a version of Caddy with plugins, head over to the download page and note down the plugins you want. Say, for example, you want to install the http.ipfilter and http.ratelimit plugins. Then, run the script like so:

sudo ./getcaddy personal http.ipfilter,http.ratelimit

Now, the script will download the binary and install it in /usr/local/bin/caddy. Now, if you type in caddy from the command line, Caddy will start serving files in the current directory on port 2015. You can open http://<your_server_ip>:2015/ in a browser to verify this.

As we’ve seen, getting started with Caddy is easy. However, to use it as a general purpose web server, we need to configure it to start automatically when the server boots up. The installation script doesn’t set this up, so we’ll manually do it.

Setting up user accounts and directories

The first step is to create a restricted user account for Caddy to run in. In our example, we’ll create an user named caddy with the home directory set to /opt/caddy using:

sudo useradd -rmd /opt/caddy caddy

The /opt/caddy directory will store various server related files, such as configuration files and website data. In addition, we’re going to create:

  • /opt/caddy/store to store SSL certificates and other persisted state files.
  • /opt/caddy/logs to store server logs.

To create these directories, run:

sudo mkdir /opt/caddy/store /opt/caddy/logs

You should also transfer the ownership of everything inside /opt/caddy to the caddy user so that the server can read and modify all files inside it with:

sudo chown -R caddy: /opt/caddy

Auto-starting Caddy

Now that we’ve the users and directories set up, we have to set up Caddy to run automatically when the server starts. However, first you need to find out the init system that’s in use. To do this run (source):

if [[ `/sbin/init --version` =~ upstart ]]; then echo upstart;
elif [[ `systemctl` =~ -\.mount ]]; then echo systemd;
elif [[ -f /etc/init.d/cron && ! -h /etc/init.d/cron ]]; then echo using sysvinit;
else echo cannot tell; fi

If the output is upstart or sysvinit, you should use the instructions for sysvinit. If the output is systemd, you should use the systemd instructions.

In the file, we’re going to specify to read the configuration file (“Caddyfile”) from /opt/caddy/Caddyfile. We’re also going to make Caddy use the folders in /opt/caddy , for logging and storage.

systemd systems

Go ahead and add a file in /etc/systemd/system/caddy.service with the following contents:

[Unit]
Description=Caddy HTTP/2 web server

[Service]
User=caddy
Group=caddy
Environment=CADDYPATH=/opt/caddy/store
ExecStart=/usr/local/bin/caddy -agree=true -log=/opt/caddy/logs/caddy.log -conf=/opt/caddy/Caddyfile -root=/dev/null
ExecReload=/bin/kill -USR1 $MAINPID
LimitNOFILE=1048576
LimitNPROC=64

[Install]
WantedBy=multi-user.target

Then, you need to enable the service with:

sudo systemctl enable caddy.service

sysvinit systems

For systems using sysvinit, you need to create the following shell script in /etc/init.d/caddy:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          caddy
# Required-Start:    $local_fs $network $named $time $syslog
# Required-Stop:     $local_fs $network $named $time $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the caddy web server
# Description:       starts caddy using start-stop-daemon
### END INIT INFO

DESC="web server"
NAME=caddy
DAEMON=/usr/local/bin/caddy
DAEMONUSER=caddy
PIDFILE=/opt/caddy/store/$NAME.pid
LOGFILE=/opt/caddy/logs/$NAME.log
CONFIGFILE=/opt/caddy/Caddyfile
DAEMONOPTS="-agree=true -pidfile=$PIDFILE -log=$LOGFILE -conf=$CONFIGFILE"
USERBIND="setcap cap_net_bind_service=+ep"
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"

test -x $DAEMON || exit 0

export CADDYPATH=/opt/caddy/store

# Set the ulimits
ulimit -n 8192


start() {
    $USERBIND $DAEMON
    start-stop-daemon --start --quiet --make-pidfile --pidfile $PIDFILE \
        --background --chuid $DAEMONUSER --oknodo --exec $DAEMON -- $DAEMONOPTS
}

stop() {
    start-stop-daemon --stop --quiet --pidfile $PIDFILE --retry=$STOP_SCHEDULE \
        --name $NAME --oknodo
    rm -f $PIDFILE
}

reload() {
    start-stop-daemon --stop --quiet --signal USR1 --pidfile $PIDFILE \
        --name $NAME
}

status() {
    if [ -f $PIDFILE ]; then
        if kill -0 $(cat "$PIDFILE"); then
            echo "$NAME is running"
        else
            echo "$NAME process is dead, but pidfile exists"
        fi
    else
        echo "$NAME is not running"
    fi
}

case "$1" in
    start)
        echo "Starting $NAME $DESC"
        start
    ;;
    stop)
        echo "Stopping $NAME $DESC"
        stop
    ;;
    restart)
        echo "Restarting $NAME $DESC"
        stop
        start
    ;;
    reload)
        echo "Reloading $NAME configuration"
        reload
    ;;
    status)
        status
    ;;
    *)
        echo "Usage: $0 {start|stop|restart|reload|status}"
        exit 2
    ;;
esac

exit 0

Then, you can enable the service with:

sudo chmod +x /etc/init.d/caddy
sudo update-rc.d caddy defaults

Creating a basic Caddyfile

Now, we have to create a Caddyfile in order to serve our website. Create /opt/caddy/Caddyfile with a text editor and type in:

http:// {
  root /opt/caddy/web/default
  log /opt/caddy/logs/default.log
  gzip
}

This will serve the contents of /opt/caddy/web/default over port 80. The request log for this website will be stored in /opt/caddy/logs/default.log. Moreover, because we’ve used the gzip directive, text-based files will be compressed with gzip and served, thus saving bandwidth.

Of course, since the webserver isn’t running yet, you have to start it up. On a distribution with systemd, run:

sudo systemctl start caddy.service

On sysvinit/upstart based distros, run:

sudo service caddy start

If you want to check if things are working as intended, add a file named index.html to the /opt/caddy/web/default directory. Now, if you open your website in a browser, you’ll see that the index.html file is being served.

Serving multiple websites

If you want to host multiple websites, that’s possible too! You simply need to add multiple blocks like this:

http://example.com {
  root /opt/caddy/web/example
  log /opt/caddy/logs/example.log
  gzip
}

http://otherwebsite.com {
  root /opt/caddy/web/otherwebsite
  log /opt/caddy/logs/otherwebsite.log
  gzip
}

http:// {
  root /opt/caddy/web/default
  log /opt/caddy/logs/default.log
  gzip
}

When a visitor visits your server with example.com, Caddy will serve them files from /opt/caddy/web/example. Similarly, when they access your website with otherwebsite.com, Caddy will serve them files from /opt/caddy/web/otherwebsite. Finally, the last block with http:// acts as a “fallback” — if a visitor visits it using any other hostname (such as directly accessing with the IP) Caddy will serve them files from /opt/caddy/web/default. You can remove this block if you don’t want the “fallback” functionality.

When you change the configuration file, you should restart the server so that the changes come into effect. On a systemd distribution, type in:

sudo systemctl restart caddy.service

For sysvinit/upstart-based distributions, the command is different:

sudo service caddy restart

HTTPS configuration

One of the best features of Caddy is its integration with Let’s Encrypt, an automated certification authority. Thus, you can enable HTTPS for your websites very easily.

Before you begin, you need to have a domain name. This is because Let’s Encrypt can issue SSL certificates for domains only.

You need to add a HTTPS block like the example shown below:

https://example.com {
        root /opt/caddy/web/example
        log /opt/caddy/logs/example.log
        gzip
        tls [email protected]
}

In the example above, we’ve added a tls directive with an email address. Let’s Encrypt’s servers will send you expiration notices to this address. Once you restart your server, Caddy will automatically contact the Let’s Encrypt servers, and deploy HTTPS on your site. There’s no need for a HTTP block — HTTP requests are redirected to HTTPS automatically.

Serving some real content

With our current setup, you can serve static files just fine. However, that isn’t very useful. Let’s look at some small examples of serving some real content — and that’s where Caddy shines!

Serving markdown files

Markdown is a lightweight markup language that translates to HTML. With Caddy, you can serve markdown files with a one-liner. Modify the block in your Caddyfile like so:

http:// {
    # other directives
    markdown /
}

Now, Caddy will serve any file with a .md, .mdown or a .markdown extension as a regular web page. What’s more, you can place a file named index.md in a directory  — it automatically becomes the default document for that directory.

For more details such as how to use your own templates, head over to the official documentation.

The file browser

Most other servers display file listings that look rather bland. Caddy has a much better looking file listing page, and it even has incremental search! You can enable this with the browse directive:

http:// {
    # other directives
    browse /my_files
}

This enables the file listing for the /my_files directory, and here’s what it looks like when you open it in a browser:

PHP files

So, what about the PHP-powered website you’ve got? That’s fairly easy to configure too.

First, you need to install PHP-FPM on your server — it’s available as a package on most distributions. Then, you need to configure the PHP-FPM daemon to run as the user caddy, and to listen on 127.0.0.1:9000.

The exact file you have to edit varies among distributions. On Debian and Ubuntu, it’s usally a file named /etc/php/<version_number>/fpm/pool.d/www.conf. On CentOS/RHEL, you have to edit /etc/php-fpm.d/www.conf. After opening the file, you need to change the values like so:

user = caddy
group = caddy
listen = 127.0.0.1:9000

Now, in your Caddyfile, put in the following directive:

http:// {
    # other directives
    fastcgi / localhost:9000 php
}

This will pass any files with the .php extension to PHP-FPM. You can easily verify if it’s working by pointing your browser to a PHP file.

Conclusion

As we’ve seen, Caddy makes it extremely easy to configure and host websites quickly. If you would like to know more,  here’s an “essential reading” list for you:

Would you give Caddy a try for your next website? What was your experience like? Did you run into any problems? Let us know in the comments below.

If you liked this post, please share it 🙂

You may also like...