Back to tutorials
Tutorial

Set up Caddy Server on AlmaLinux 10

In this tutorial, we'll learn how to set up Caddy Server on AlmaLinux 10. Caddy is a modern, open-source web server written in Go, manages HTTPS certificates.

By Anurag Singh
Updated on Oct 30, 2025
Category: Tutorial
Share article
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.