There is a new version of this tutorial available for Ubuntu 22.04 (Jammy Jellyfish).

How to Install and Configure Caddy Web Server with PHP and MariaDB on Ubuntu 20.04

Caddy Web Server is a modern open-source web server written in GO language. It doesn't have any dependencies and runs off of a static binary file and generates and renews SSL certificates automatically. It can work as a static file server, scalable reverse proxy or a powerful dynamic server and can be expanded via plugins. It also includes support for HTTP/2 and experimental HTTP/3 protocols.

In this tutorial, you'll install and configure Caddy to run along with PHP and MariaDB on an Ubuntu 20.04 based server.

Prerequisites

  • An Ubuntu 20.04 server with a non-root account having sudo privileges.
  • A fully registered domain name pointed to your server's IP address.

Step 1 - Configure Firewall

If you are using UFW (Uncomplicated Firewall), you need to configure it to allow access to HTTP and HTTPS ports for the server to work.

Check the status of the firewall.

$ sudo ufw status

You should see something like the following.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

Allow HTTP and HTTPs ports.

$ sudo ufw allow http
$ sudo ufw allow https

Check the status again to confirm.

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

Step 2 - Install Caddy

There are several ways to install Caddy which includes a docker installation, using official repositories or building from source. The building from source method is useful if you want functionality which can only be added via a 3rd party module/plugin.

For the purpose of this tutorial, we will stick to using the official Caddy repository for Ubuntu/Debian based systems.

Add the Repository to the list.

$ echo "deb [trusted=yes] https://apt.fury.io/caddy/ /" | sudo tee -a /etc/apt/sources.list.d/caddy-fury.list
$ sudo apt update

This will create a caddy repository file in the /etc/apt/sources.list.d directory with the location to the Caddy's repository.

Install Caddy.

$ sudo apt install caddy

This will automatically install and run the Caddy web server. Open http:// in your browser and you should be greeted by the following page.

Caddy Default Page

Step 3 - Install PHP

Install Ondrej's PHP repository.

$ sudo add-apt-repository ppa:ondrej/php

Install PHP 7.4 along with some additional packages.

$ sudo apt install php-cli php-fpm php-mysql

Check if PHP is working correctly.

$ php --version

You should see a similar output.

PHP 7.4.6 (cli) (built: May 14 2020 10:03:35) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies

Step 4 - Install MariaDB

MariaDB is a drop-in replacement for MySQL which means commands to run and operate MariaDB are the same as those for MySQL.

Add Mariadb's Official repository. You can opt for a different mirror which is closer to your server's location from MariaDB's repository page.

$ sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
$ sudo add-apt-repository 'deb [arch=amd64] http://mirror.lstn.net/mariadb/repo/10.4/ubuntu f
ocal main'

To install MariaDB issue the following command.

$ sudo apt install mariadb-server

Check if MariaDB installed correctly.

$ mysql --version

You should see the following output.

mysql  Ver 15.1 Distrib 10.4.13-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

Enable the MariaDB service.

$ sudo systemctl enable mariadb

Step 5 - Configure MariaDB

Run the following command to perform default configuration such as giving a root password, removing anonymous users, disallowing root login remotely and dropping test tables.

$ sudo mysql_secure_installation

With MariaDB 10.4, you will now be asked between using the root password or unix_socket plugin. The unix_socket plugin allows you to log in to MariaDB with your Linux user credentials. It is considered more secure though you will need a traditional username/password to use 3rd party apps like phpMyAdmin. We will stick to using unix_socket plugin for this tutorial. You can still use phpMyAdmin via any user you specific user you create for your databases.

Pressing Enter chooses the default option (the one that is capitalised, Y in this case).

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
haven't set the root password yet, you should just press enter here.

Enter current password for root (enter for none): [PRESS ENTER]
OK, successfully used password, moving on...

Setting the root password or using the unix_socket ensures that nobody
can log into the MariaDB root user without the proper authorisation.

You already have your root account protected, so you can safely answer 'n'.

Switch to unix_socket authentication [Y/n] [PRESS ENTER]
Enabled successfully!
Reloading privilege tables..
 ... Success!

You already have your root account protected, so you can safely answer 'n'.

Change the root password? [Y/n] [ANSWER n]
... skipping.

By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] [PRESS ENTER]
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] [PRESS ENTER]
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] [PRESS ENTER]
 \- Dropping test database...
 ... Success!
 \- Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] [PRESS ENTER]
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

Log in to the MariaDB's SQL Shell.

$ sudo mysql

Enter your root password when prompted.

Create a test database and user with access permission. Replace database and user with your choice. Replace password with a strong password.

CREATE DATABASE testdb;
CREATE USER 'user' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON testdb.* TO 'user';

Exit the MySQL Shell.

exit

Step 6 - Configure Caddy

Caddy can be configured in several ways - API calls, JSON file or a Caddyfile. Caddyfile is the easiest way to configure Caddy which we will use in our tutorial.

Before we configure Caddy, we should create the root directory for our site.

$ sudo mkdir -p /var/www/example.com/html

-p flag creates any missing parent directories as well.

We also need to create a directory to store the log files for Caddy.

$ sudo mkdir /var/log/caddy

Caddy server on installation creates a user caddy which handles its tasks for it. We need to give permissions to the log directory so that Caddy can access and write to it.

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

During installation, Caddy generated a default Caddyfile at /etc/caddy/Caddyfile. Open it.

$ sudo nano /etc/caddy/Caddyfile

It should look something like the following.

# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace the line below with your
# domain name.
:80

# Set this path to your site's directory.
root * /usr/share/caddy

# Enable the static file server.
file_server

# Another common task is to set up a reverse proxy:
# reverse_proxy localhost:8080

# Or serve a PHP site through php-fpm:
# php_fastcgi localhost:9000

# Refer to the Caddy docs for more information:
# https://caddyserver.com/docs/caddyfile

:80 tells Caddy to serve everything over port number 80. root sets the path for your site's home directory. file_server enables Caddy to run as a static file server.

Replace the above code with the following code.

{
	experimental_http3
}
example.com {
    root * /var/www/example.com/html
    log {
        output file /var/log/caddy/example.com.access.log {
        	roll_size 3MiB
	        roll_keep 5
	        roll_keep_for 48h
        }
        format console
    }
    encode gzip zstd
    php_fastcgi unix//run/php/php7.4-fpm.sock
   
    tls [email protected] {
    	protocols tls1.2 tls1.3    	
    }
}

Let us go through all sections of the Caddyfile below.

  • The first block in any Caddyfile is a global block unless you specify a hostname which then it becomes a site block. A global block carries directives that are applied to all sites that you host under the server. In our example, we have enabled support for the experimental HTTP/3 protocol. The global block is completely optional and you can always start your Caddyfile with the site block directly.
  • Next block is the site block. If you have just 1 site on your server, you don't need to enclose your configuration in a block but if you are going to host multiple sites, you should host each site's configuration in its own block. A site block is marked by curly brackets. Every site block starts with the hostname of the site.
  • log enables and configures HTTP request logging. Without the log directive, Caddy won't log anything. output configures where to write the log file to. format describes how to encode, or format, the logs. The console formats the log entry for human readability.
  • encode directive here enables Gzip and Zstandard compression for the site.
  • php_fastcgi proxies requests to a PHP FastCGI server such as php-fpm. Here we are listening to requests over a Unix socket.
  • tls block configures settings related to SSL certificates and security. Here, we have enabled support for both TLSv1.2 and TLSv1.3 protocols. By default, Caddy supports TLS v1.2 out of the box. Caddy also generates SSL certificates automatically for all sites. If you don't want Caddy to generate SSL for you, you can do so by either using the IP address instead of the hostname or specify the full URL i.e. http://example.com. In such cases, Caddy won't generate the SSL certificate.

So far we have covered the absolute basics of writing a Caddyfile which should help you get started. You can read more about it in the official documentation.

Step 7 - Configure PHP

Now, that our Caddyfile is ready, it is time to configure PHP.

First, we have to change the username for the PHP process. Open the file /etc/php-fpm.d/www.conf.

$ sudo nano /etc/php/7.4/fpm/pool.d/www.conf

Find the user=www-data and group=www-data lines in the file and change them to caddy.

...
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
; RPM: apache user chosen to provide access to the same directories as httpd
user = caddy
; RPM: Keep a group allowed to write in log dir.
group = caddy
...

Also, find the lines listen.owner=www-data and listen.group=www-data in the file and change them to caddy.

listen.owner = caddy
listen.group = caddy

Save the file by pressing Ctrl+X and entering Y when prompted.

Restart the PHP-fpm process.

$ sudo systemctl restart php7.4-fpm

Step 8 - Launch Demo Site

Now that we have created a Caddyfile and configured PHP to run with the server, it is time to create and launch a demo website.

Make sure your domain name is pointed to the server's IP address.

Restart Caddy server to apply the changes in the Caddyfile we created above.

$ sudo systemctl restart caddy

Check the status of the Caddy server to make sure it is working properly.

$ sudo systemctl status caddy
? caddy.service - Caddy
     Loaded: loaded (/lib/systemd/system/caddy.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2020-05-20 07:09:25 UTC; 2s ago
       Docs: https://caddyserver.com/docs/
   Main PID: 25410 (caddy)
      Tasks: 7 (limit: 1074)
     Memory: 17.8M
     CGroup: /system.slice/caddy.service
             ??25410 /usr/bin/caddy run --environ --config /etc/caddy/Caddyfile

May 20 07:09:25 example.com caddy[25410]: 2020/05/20 07:09:25 [INFO][example.com] Obtain: Lock acquired; proceeding...
May 20 07:09:25 example.com caddy[25410]: 2020/05/20 07:09:25 [INFO][cache:0xc0006f8cd0] Started certificate maintenance routine
May 20 07:09:25 example.com caddy[25410]: 2020/05/20 07:09:25 [INFO] acme: Registering account for [email protected]
May 20 07:09:26 example.com caddy[25410]: 2020/05/20 07:09:26 [INFO][example.com] Waiting on rate limiter...
May 20 07:09:26 example.com caddy[25410]: 2020/05/20 07:09:26 [INFO][example.com] Done waiting
May 20 07:09:26 example.com caddy[25410]: 2020/05/20 07:09:26 [INFO] [example.com] acme: Obtaining bundled SAN certificate given a CSR
May 20 07:09:27 example.com caddy[25410]: 2020/05/20 07:09:27 [INFO] [example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/4696123289
May 20 07:09:27 example.com caddy[25410]: 2020/05/20 07:09:27 [INFO] [example.com] acme: use tls-alpn-01 solver
May 20 07:09:27 example.com caddy[25410]: 2020/05/20 07:09:27 [INFO] [example.com] acme: Trying to solve TLS-ALPN-01
May 20 07:09:27 example.com caddy[25410]: 2020/05/20 07:09:27 http: TLS handshake error from 127.0.0.1:39466: EOF

You can see from above that Caddy Generated the certificate automatically when we first restarted the server after creating the Caddyfile.

Let us create a test page to verify that Caddy can render PHP and connect to the MariaDB database.

$ sudo nano /var/www/example.com/html/test.php

Paste the following code in the editor. Replace "user" and "password" fields with the MariaDB credentials you created before.

<html>
<head>
    <title>Caddy Demo Site</title>
    <style type="text/css">
        #wrap {
            width: 900px;
            margin: 0 auto;
        }
    </style>
</head>
<body id="wrap">
    <h2>Caddy Demo Site</h2>
    <?php echo '<p>Hello,</p>';

    // Define PHP variables for the MySQL connection.
    $servername = "localhost";
    $username = "user";
    $password = "password";

    // Create a MySQL connection.
    $conn = mysqli_connect($servername, $username, $password);

    // Report if the connection fails or is successful.
    if (!$conn) {
        exit('<p>Your connection has failed.<p>' .  mysqli_connect_error());
    }
    echo '<p>You have connected successfully.</p>';
    ?>
</body>
</html>

Save the file by pressing Ctrl+X and entering Y when prompted.

Visit https://example.com/test.php in a web browser. You should get the following page.

Caddy Demo Site Page

If you see an error message or if the page does not load at all, re-check your configuration

Remove the test file once you are satisfied.

$ sudo rm /var/www/example.com/html/test.php

Conclusion

This concludes our tutorial where we installed Caddy web server along with PHP and MariaDB SQL. If you have any queries, ask them in the comments below.

Share this page:

7 Comment(s)