Set up Caddy Server on AlmaLinux 10

By Anurag Singh

Updated on Oct 30, 2025

Set up Caddy Server on AlmaLinux 10

In this tutorial, we'll learn how to set up Caddy Server on AlmaLinux 10.

What is Caddy?

Caddy is a modern, open-source web server written in Go. It’s designed to be simple, secure, and fully automated. What makes Caddy special is that it automatically manages HTTPS certificates using Let’s Encrypt — no manual setup or renewal needed. It can serve static websites, act as a reverse proxy for backend apps, and handle load balancing with clean, human-readable configuration. In short, Caddy turns complex server management into a one-file setup that just works.

Prerequisites

Before we begin, ensure we have the following:

  • An AlmaLinux 10 dedicate server or KVM VPS.
  • Basic Linux Command Line Knowledge.
  • A domain name, pointing A record to server IP.

Set up Caddy Server on AlmaLinux 10

1) System update and minimal tools

sudo dnf update -y
sudo dnf install -y dnf-plugins-core policycoreutils-python-utils

2) Install Caddy (official COPR package for RHEL-family distros)

Caddy provides an official COPR repository for RHEL/Fedora families — easiest and recommended for production on AlmaLinux. 

# Enable COPR and install
sudo dnf copr enable @caddy/caddy -y
sudo dnf install -y caddy

Confirm:

caddy version
# or
caddy -v

3) Configure Caddy

Open the main configuration file:

sudo nano /etc/caddy/Caddyfile

Replace its content with this simple setup:

example.com {
    root * /var/www/example
    file_server
}

Important: Replace example.com with our real domain that points to the server’s public IP address.

For proxy server

We proxy example.com to a backend running on 127.0.0.1:3000.

Edit /etc/caddy/Caddyfile:

nano /etc/caddy/Caddyfile

Add following content:

example.com {
    encode zstd gzip
    tls {
        # leave blank — Caddy will manage ACME certificates automatically
    }

    # Reverse proxy to local app
    reverse_proxy 127.0.0.1:3000 {
        header_up Host {http.reverse_proxy.upstream.hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }

    log {
        output file /var/log/caddy/access.log {
            roll_size 50mb
            roll_keep 5
            roll_keep_for 336h
        }
        level INFO
    }
}

Notes: tls left default so Caddy uses ACME to provision certs automatically; encode enables compression; log file path and rotation are practical defaults.

Validate the Caddyfile:

sudo caddy validate --config /etc/caddy/Caddyfile

4) Create directories + permissions

Caddy runs as user caddy when installed from packages. Ensure directories and ownership are correct:

sudo mkdir -p /var/www/example.com
sudo chown -R caddy:caddy /var/www/example.com
sudo mkdir -p /var/log/caddy
sudo chown -R caddy:caddy /var/log/caddy

If we store site assets under /srv or another location, adapt ownership similarly.

5) Enable and start the Caddy systemd service

When installed via packages, Caddy ships systemd unit files. Start and enable:

sudo systemctl daemon-reload
sudo systemctl enable --now caddy.service
sudo systemctl status --no-pager --full caddy

If the service fails, inspect journal logs:

sudo journalctl -u caddy -e

Caddy will attempt ACME certificate issuance on first start for the configured hostnames. 

6) Open firewalld ports (HTTP/HTTPS)

We must allow ports 80 and 443 in firewalld so ACME (HTTP challenge) and browser traffic work:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# verify
sudo firewall-cmd --list-all

If firewalld is not used, open those ports on whichever firewall/NAT appliance is in front of the server. 

7) SELinux considerations (AlmaLinux default)

AlmaLinux ships SELinux enabled. Caddy may need limited SELinux allowances to:

  • Allow outbound network connections for ACME challenges and to reach upstream backends.
  • Ensure Caddy can write logs and access content directories under expected contexts.

Common fixes:

Permit outbound HTTP/S connections for webserver-type services:

# allow processes with httpd_t-like privileges to make network connections

sudo setsebool -P httpd_can_network_connect 1

Ensure file contexts for log and content directories are correct, then restore contexts:

# set proper types if needed, example for /var/www
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www(/.*)?"
sudo restorecon -Rv /var/www

# logs
sudo semanage fcontext -a -t var_log_t "/var/log/caddy(/.*)?"
sudo restorecon -Rv /var/log/caddy]

If SELinux denies are still seen, check sudo ausearch -m avc -ts recent or sudo journalctl -k and use audit2allow to craft precise rules. 

If we cannot resolve a permission quickly in a non-production lab, temporarily set SELinux to permissive to diagnose (not recommended for long-term production):

sudo setenforce 0    # temporary (reverts on reboot)

Permanently edit /etc/selinux/config with SELINUX=permissive (if needed)

8) Verify HTTPS and functionality

Open a browser to https://example.com — we expect a lock icon and valid certificate.

From the server:

curl -I https://example.com

Check Caddy logs:

sudo tail -n 200 /var/log/caddy/access.log
sudo journalctl -u caddy -f

If Caddy fails to obtain a cert, typical root causes are:

  • DNS not pointing at server IP (ACME validation fails).
  • Port 80 blocked by cloud provider or NAT.
  • SELinux blocking outbound requests or file access.

Multiple services already bound to ports 80/443 (nginx/apache). Caddy needs these ports for ACME HTTP challenge and HTTP->HTTPS redirect. 

Security & production tips

  • Run Caddy as the packaged caddy user (default) — do not run as root. Package takes care of that.
  • Use a short health_path for backends so Caddy can detect failed backends and avoid routing to them.
  • Keep backups of /etc/caddy/Caddyfile and any TLS-related environment variables for DNS plugins.
  • For strict compliance environments, pin ACME issuer or use a private CA via tls directive.

Conclusion

We set up Caddy from the official repo, created a Caddyfile for reverse proxying apps and static content, confirmed automatic HTTPS behavior, covered wildcard/DNS challenge considerations, and included checks and production tips. Caddy removes a lot of the friction around TLS and reverse proxies; for most web apps it’s a lighter, simpler alternative to manual cert management and complex Nginx configs. Use the examples above as copy-paste templates and adapt headers/backends to our stack.