Setting up Caddy PHP MySQL stack for a web server

Login into your server as the root user ssh

Update everything first

apt update && apt upgrade

Set timezone to UTC

dpkg-reconfigure tzdata and select UTC

Set hostname

hostnamectl set-hostname caddy-prod

Setup user

Add a new user for ourselves, we won’t be using root user after this initial setup for security purposes.

adduser ashfame
usermod -aG sudo ashfame

Setup SSH key based login for ourselves

mkdir ~/.ssh; nano ~/.ssh/authorized_keys

Paste your public key and save the file. Set permissions.

sudo chmod -R 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys

Now update SSH config

In /etc/ssh/sshd_config

PermitRootLogin no
PasswordAuthentication no
AddressFamily inet #to allow ipv4 only for ssh connection

Restart SSH service to bring the changs into effect

sudo systemctl restart sshd

Ensure you can login with your new user without a password and just by your key before killing your root user session.

Setup Firewall

ufw app list
ufw allow OpenSSH
ufw allow http
ufw allow https
ufw enable
ufw status

Install packages

Install Caddy

For installing Caddy, lets first install go lang so that we can compile from source, right here on the server (if needed)

Install Go lang

Download the latest archive file

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.linux-amd64.tar.gz

Add PATH=$PATH:/usr/local/go/bin in $HOME/.profile

Clone Caddy from Github for compilation and installation

git clone
cd caddy/cmd/caddy/
go build
sudo mv caddy /usr/bin/
caddy version
sudo chown root:root /usr/bin/caddy
sudo chmod 755 /usr/bin/caddy

Create Caddy user and group

sudo groupadd --system caddy

sudo useradd --system \
--gid caddy \
--create-home \
--home-dir /var/lib/caddy \
--shell /usr/sbin/nologin \
--comment 'Caddy web server' \

Set Caddy to run via systemd

Take systemd service file –
and put that at /etc/systemd/system/caddy.service

Define Caddyfile at /etc/caddy/Caddyfile


response "Hello world"

Now, test by loading your IP address of your server and you should see some “hello world” output defined in your Caddyfile

Install PHP8

sudo apt install php8.1-fpm php8.1-cli php8.1-common php8.1-mysql php8.1-zip php8.1-gd php8.1-mbstring php8.1-curl php8.1-xml php8.1-bcmath php8.1-opcache php8.1-readline

Install MariaDB for database in place of MySQL

sudo apt install mariadb-server
sudo mysql_secure_installation
sudo mariadb

Run the following SQL commands:

GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
sudo systemctl status mariadb
sudo mysqladmin version

Create more databases and a user for each of them (`sudo mariadb`):

create database food_ashfame_com
CREATE USER 'food_ashfame_com'@'localhost' IDENTIFIED BY 'password';
GRANT ALL ON food_ashfame_com.* TO 'food_ashfame_com'@'localhost' WITH GRANT OPTION;

Create php-fpm user

sudo useradd --system \
--gid caddy \
--shell /usr/sbin/nologin \
--comment 'PHP-FPM user' \

In /etc/php/8.1/fpm/pool.d/www.conf, change:

user = php-fpm
group = caddy

# For socket
listen.owner = caddy = caddy
listen.mode = 0660

Configure log file in /etc/php/8.1/fpm/php.ini using error_log
error_log = /var/log/php_errors.log

All configured/resultant config can be viewed in phpinfo()‘s output

File permissions

Since we have configured php to run as php-fpm user and its in caddy group,
owners of all our files could be ashfame and group as caddy
Permission as follows:

sudo find . -type f -exec chmod 640 {} +
sudo find . -type d -exec chmod 751 {} +
sudo chmod 640 wp-config.php
sudo chmod 660 wp-content/debug.log

This is tight enough to not allow WP upgrades, plugin installs etc to not work. Media uploads can be made to work with the following permission:

There are more things to do, like configure Caddyfile to run your WordPress sites and other services, which I might follow up on later in separate posts.


Leave a Reply

Your email address will not be published. Required fields are marked *