SSL Certificates on Bare Metal: Let’s Encrypt, Certbot, and Auto-Renewal
Every website needs HTTPS. Managed platforms handle SSL automatically, but on bare metal you own the process. The good news: Let’s Encrypt provides free certificates, Certbot automates everything, and you can score an A+ on SSL Labs in under ten minutes.
Why SSL Matters
Without HTTPS, browsers show “Not Secure” warnings that kill user trust. Google uses HTTPS as a ranking signal. HTTP/2 and HTTP/3 require TLS. There is no reason to skip it, especially when certificates cost nothing.
Managed platforms like Vercel and Cloudflare handle SSL transparently. On bare metal, you run Certbot once, set up a cron job, and never think about it again.
Step 1: Install Certbot
# Deploy a RAW server
npx rawhq deploy
# SSH in and install Certbot with the Nginx plugin
ssh root@your-server-ip
apt update && apt install -y certbot python3-certbot-nginxCertbot is the official Let’s Encrypt client. The Nginx plugin automatically modifies your Nginx config to serve certificates.
Step 2: Get Your Certificate
Make sure your domain’s DNS A record points to your server IP, then run:
# Obtain certificate (replace with your domain)
certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Certbot will:
# 1. Verify domain ownership via HTTP challenge
# 2. Download the certificate
# 3. Update your Nginx config
# 4. Reload NginxThat is it. Your site is now serving HTTPS. Certbot adds the ssl_certificate and ssl_certificate_key directives to your Nginx server block automatically.
Step 3: Nginx SSL Configuration
Certbot configures the basics, but you should harden the defaults for an A+ rating. Edit your Nginx config:
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Strong SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS (force HTTPS for 1 year)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
}Step 4: Auto-Renewal
Let’s Encrypt certificates expire every 90 days. Certbot installs a systemd timer by default, but you can verify it:
# Check the renewal timer
systemctl status certbot.timer
# Test renewal (dry run)
certbot renew --dry-run
# If the timer is missing, add a cron job
echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'" | crontab -The --post-hook flag reloads Nginx after a successful renewal so the new certificate takes effect immediately.
Step 5: Test with SSL Labs
Go to ssllabs.com/ssltest and enter your domain. With the configuration above, you should score A+. Key checks:
- Certificate chain is complete (fullchain.pem includes intermediate certs)
- TLSv1.0 and TLSv1.1 are disabled
- HSTS header is present
- OCSP stapling is enabled
- No weak ciphers (RC4, 3DES, etc.)
Redirect HTTP to HTTPS
Force all traffic to HTTPS with a redirect block:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://yourdomain.com$request_uri;
}Certbot usually adds this automatically. Verify by visiting http://yourdomain.com and confirming the redirect.
Wildcard Certificates
Need SSL for subdomains like api.yourdomain.com and app.yourdomain.com? Use a wildcard certificate with DNS validation:
# Wildcard cert (requires DNS challenge)
certbot certonly --manual --preferred-challenges dns \
-d "*.yourdomain.com" -d yourdomain.comCertbot will ask you to create a TXT record. For automated DNS challenges, use a plugin like certbot-dns-cloudflare or certbot-dns-route53.
Bare Metal vs Managed SSL
Managed SSL is convenient but gives you zero control over cipher suites, HSTS settings, or certificate pinning. On bare metal, you own the entire TLS stack.
Common Pitfalls
- Firewall blocking port 80 — Certbot needs port 80 open for the HTTP challenge
- DNS not propagated — Wait for A record propagation before running Certbot
- Forgetting renewal — Always verify the systemd timer or cron job exists
- Using self-signed certs — Browsers reject them. Use Let’s Encrypt instead
Get Started
Deploy a RAW server, install Certbot, and you have free, auto-renewing HTTPS in under ten minutes. No managed platform required.