Rocky 9: nginx TLS with a self-signed certificate
Test scope
Section titled “Test scope”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.
Generate a self-signed certificate
Section titled “Generate a self-signed certificate”sudo dnf install -y nginx opensslsudo 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.crtSelf-signed certificates are for testing only; browsers will report them as untrusted. Production needs an ACME-issued certificate.
Write the 8443 server block
Section titled “Write the 8443 server block”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.
Validate before starting
Section titled “Validate before starting”sudo nginx -tsudo nginxnginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successfulnginx -t checks certificate paths and key matching; test before reloading, otherwise one reload can take production down.
Verify the handshake
Section titled “Verify the handshake”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"okNew, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384Protocol : TLSv1.3curl -k skips certificate validation (a self-signed cert always fails it); s_client shows the negotiated protocol and cipher.
Let’s Encrypt (untested)
Section titled “Let’s Encrypt (untested)”sudo dnf install -y epel-releasesudo dnf install -y certbot python3-certbot-nginxsudo certbot --nginx -d example.comsudo certbot renew --dry-runKey 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.
Common traps
Section titled “Common traps”- Private key permissions:
lab.keyshould 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.
Practice: run a self-signed 8443
Section titled “Practice: run a self-signed 8443”sudo dnf install -y nginx opensslsudo 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.crtsudo 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"; }}EOFsudo nginx -t && sudo nginxcurl -sk https://127.0.0.1:8443/Checks:
nginx -treports successfulcurl -skreturnsokopenssl s_clientshowsTLSv1.3- Breaking the certificate path makes
nginx -tfail (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.
Next steps
Section titled “Next steps”- Opening ports: firewall-cmd rules
- Container services: Quadlet services
- Post-install checks: 7 checks after install