Skip to content
Search Rocky Linux

    Browse this documentation

    Rocky 9: nginx TLS with a self-signed certificate

    In a Rocky Linux 9.8 privileged container (nginx 1.20.1, OpenSSL): generated a self-signed certificate with openssl req -x509, wrote an 8443 server in conf.d, ran nginx -t, started nginx, tested with curl -k and openssl s_client. Log: plans/content-expansion/evidence/logs/rocky-nginx-tls.log.

    Let’s Encrypt / certbot was not tested here (it needs a public domain and ports 80/443); that section follows the official flow.

    Terminal window
    sudo dnf install -y nginx openssl
    sudo openssl req -x509 -nodes -newkey rsa:2048 -days 30 \
    -subj "/CN=lab.local" \
    -keyout /etc/pki/tls/private/lab.key \
    -out /etc/pki/tls/certs/lab.crt
    -rw-r--r-- 1 root root 1115 ... /etc/pki/tls/certs/lab.crt

    Self-signed certificates are for testing only; browsers will report them as untrusted. Production needs an ACME-issued certificate.

    server {
    listen 8443 ssl;
    server_name lab.local;
    ssl_certificate /etc/pki/tls/certs/lab.crt;
    ssl_certificate_key /etc/pki/tls/private/lab.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    location / { return 200 "ok\n"; }
    }

    Place it in /etc/nginx/conf.d/lab.conf. The chain goes into ssl_certificate: nginx 1.20 on EL9 takes a single file, so concatenate the fullchain from your CA there.

    Terminal window
    sudo nginx -t
    sudo nginx
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful

    nginx -t checks certificate paths and key matching; test before reloading, otherwise one reload can take production down.

    Terminal window
    curl -sk https://127.0.0.1:8443/
    echo | openssl s_client -connect 127.0.0.1:8443 -servername lab.local 2>/dev/null | grep -E "Protocol|Cipher"
    ok
    New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
    Protocol : TLSv1.3

    curl -k skips certificate validation (a self-signed cert always fails it); s_client shows the negotiated protocol and cipher.

    Terminal window
    sudo dnf install -y epel-release
    sudo dnf install -y certbot python3-certbot-nginx
    sudo certbot --nginx -d example.com
    sudo certbot renew --dry-run

    Key points: certbot --nginx needs nginx running, the domain resolving to this host and port 80 reachable; renew --dry-run validates the renewal path. certbot comes from EPEL, so epel-release must be installed first.

    • Private key permissions: lab.key should not be world-readable; 600 and root-owned is a sane default;
    • Incomplete chain: only the leaf certificate breaks some clients; ship the fullchain;
    • Skipping nginx -t: reloading a broken config interrupts service.
    Terminal window
    sudo dnf install -y nginx openssl
    sudo openssl req -x509 -nodes -newkey rsa:2048 -days 7 -subj "/CN=lab.local" \
    -keyout /etc/pki/tls/private/lab.key -out /etc/pki/tls/certs/lab.crt
    sudo tee /etc/nginx/conf.d/lab.conf >/dev/null <<'EOF'
    server {
    listen 8443 ssl;
    server_name lab.local;
    ssl_certificate /etc/pki/tls/certs/lab.crt;
    ssl_certificate_key /etc/pki/tls/private/lab.key;
    location / { return 200 "ok\n"; }
    }
    EOF
    sudo nginx -t && sudo nginx
    curl -sk https://127.0.0.1:8443/

    Checks:

    1. nginx -t reports successful
    2. curl -sk returns ok
    3. openssl s_client shows TLSv1.3
    4. Breaking the certificate path makes nginx -t fail (the guard works)

    常见问题

    Why does curl need -k for a self-signed certificate?

    It is not in the system trust store, so curl reports a validation error. -k skips validation and is for testing only; production needs a trusted CA.

    certbot --nginx cannot find a server block?

    certbot needs a server_name matching the domain; confirm DNS, port 80 reachability and that nginx is running.

    How many files does ssl_certificate take?

    On EL9 nginx a single file is enough: concatenate the leaf and intermediate certificates (fullchain) into ssl_certificate and point ssl_certificate_key at the key.