Web Servers Azure

NGINX with Certbot SSL on Ubuntu 24.04 LTS | cloudimg

| Product: NGINX with Certbot SSL on Ubuntu 24.04 LTS on Azure

Overview

This cloudimg image ships NGINX 1.30 mainline on Ubuntu 24.04 LTS together with Certbot and the python3-certbot-nginx plugin pre-installed. A Mozilla Intermediate TLS snippet and a self-signed fallback certificate are staged in the image so https:// answers from first boot. Point a DNS A record at the VM, run one Certbot command, and you have a browser-trusted Let's Encrypt certificate with automatic renewal already scheduled — no OpenSSL wrangling, no cron edits, no ACME scaffolding to hand-write.

Prerequisites

  • An Azure subscription with permission to create virtual machines

  • An SSH key pair — the public key is uploaded during VM creation

  • A domain name you can edit DNS for — you will create an A record pointing at the VM's public IP before running Certbot

  • An email address for the Let's Encrypt account registration (also receives expiry warnings if renewal ever fails)

Step 1 — Deploy from the Azure Portal

  • Find NGINX SSL (Certbot) on Ubuntu 24.04 LTS by cloudimg in the Azure Marketplace and click Create.

  • On the Basics tab, select or create a Resource Group, choose East US as the region, and set the VM size to Standard_B2s (2 vCPU / 4 GB RAM) for a lightweight reverse proxy, or Standard_D2s_v3 (2 vCPU / 8 GB RAM) and upward for production TLS-terminating workloads.

  • Under Administrator account, choose SSH public key and paste your public key.

  • On the Networking tab, open inbound ports 80 (HTTP + ACME HTTP-01 challenge) and 443 (HTTPS) in addition to 22 (SSH). Port 80 is required — Let's Encrypt's HTTP-01 validation connects to it to confirm you control the domain.

  • Click Review + create, then Create.

Step 2 — Deploy with the Azure CLI

az vm create \
  --resource-group my-rg \
  --name nginx-ssl \
  --image "$(az sig image-version list \
       --resource-group AZURE-CLOUDIMG \
       --gallery-name cloudimgGallery \
       --gallery-image-definition nginx-ssl-certbot-ubuntu-24-04 \
       --query '[0].id' -o tsv)" \
  --size Standard_D2s_v3 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

After the VM starts, open HTTP and HTTPS:

az vm open-port --port 80  --resource-group my-rg --name nginx-ssl --priority 1010
az vm open-port --port 443 --resource-group my-rg --name nginx-ssl --priority 1011

Step 3 — Point Your DNS at the VM

Find the VM's public IP:

az vm show --resource-group my-rg --name nginx-ssl --show-details --query publicIps -o tsv

In your DNS provider's control panel, create an A record for the hostname you want to use — for example nginx-ssl.example.com — and set its value to the public IP from the previous command. Wait for propagation before running Certbot:

dig +short nginx-ssl.example.com

The command should return the VM's public IP. If it returns nothing, wait a few minutes for DNS to propagate before continuing.

Step 4 — Connect via SSH

ssh azureuser@nginx-ssl.example.com

On first boot, nginx.service and certbot.timer are both enabled. The timer is a no-op until a certificate is actually issued, so there is nothing to do on first login other than issue a cert.

Step 5 — Verify the Image is Ready

Check that NGINX is running and listening on both ports:

sudo systemctl status nginx --no-pager
sudo ss -tln | grep -E ':80|:443'

Confirm the Certbot version and the NGINX plugin are importable:

certbot --version
python3 -c 'import certbot_nginx; print("certbot_nginx plugin OK")'

Confirm that certbot.timer is enabled — this is the systemd timer that will auto-renew your certificate twice daily once you issue one:

systemctl list-timers --all | grep certbot

Fetch the fallback HTTPS page — the image ships with a self-signed certificate so https:// answers from first boot, before you have run Certbot:

curl -skI https://127.0.0.1/ | head -3

Step 6 — Issue a Let's Encrypt Certificate

This is the one command that turns the self-signed fallback into a browser-trusted Let's Encrypt certificate. Substitute your domain and email address:

sudo certbot --nginx \
  -d nginx-ssl.example.com \
  -m admin@example.com \
  --agree-tos \
  --redirect \
  --non-interactive

Certbot will:

  • Request a certificate from Let's Encrypt using the HTTP-01 challenge (which is why port 80 must be reachable from the Internet)

  • Install the issued certificate and private key under /etc/letsencrypt/live/nginx-ssl.example.com/

  • Rewrite /etc/nginx/conf.d/default.conf so :443 uses the new certificate and :80 redirects to https://

  • Reload NGINX

Expected output ends with a Successfully received certificate line and the path to the issued certificate. If this step fails, jump to Step 10 — Troubleshooting before retrying — Let's Encrypt rate-limits failed requests.

Step 7 — Verify HTTPS Redirect and New Certificate

Confirm the redirect now returns 301 to https://:

curl -sI http://nginx-ssl.example.com/ | head -3

Confirm the HTTPS response and inspect the issuer chain — it should now read Let's Encrypt rather than nginx-cloudimg-fallback:

curl -sI https://nginx-ssl.example.com/ | head -3
echo | openssl s_client -servername nginx-ssl.example.com -connect nginx-ssl.example.com:443 2>/dev/null | openssl x509 -noout -issuer -subject -dates

Step 8 — Confirm Auto-Renewal is Scheduled

certbot.timer was enabled when the image was built, so your newly-issued certificate is already registered for automatic renewal. Confirm the next fire time:

systemctl list-timers certbot.timer

Run a dry-run to prove the renewal flow works end-to-end for this specific certificate:

sudo certbot renew --dry-run

The output should include Congratulations, all simulated renewals succeeded — that confirms Certbot can reach Let's Encrypt and NGINX is reloaded correctly at renewal time. No further action is required; Let's Encrypt certificates last 90 days and Certbot will renew 30 days before expiry.

Step 9 — Publish Your Own Content

The active document root is /var/www/html. Replace the welcome page with your own HTML:

sudo tee /var/www/html/index.html > /dev/null <<'HTML'
<!doctype html>
<html><head><meta charset="utf-8"><title>My Site</title></head>
<body><h1>Hello from NGINX over HTTPS.</h1></body></html>
HTML

Reload NGINX to pick up the change:

sudo nginx -t && sudo systemctl reload nginx

Fetch the new page over HTTPS:

curl -s https://nginx-ssl.example.com/ | head -3

For reverse-proxy or load-balancer configurations, add a server block under /etc/nginx/conf.d/ and include the shared TLS params:

include /etc/nginx/snippets/ssl-params.conf;

Step 10 — Troubleshooting

Certbot fails with Connection refused on port 80. The HTTP-01 challenge needs your VM reachable on port 80 from the public Internet. Confirm the Azure NSG allows inbound 80/tcp and that nginx.service is listening:

sudo ss -tln | grep ':80 '

Certbot fails with DNS problem: NXDOMAIN. The A record has not propagated, or it points to the wrong IP. Re-run:

dig +short nginx-ssl.example.com

It must return the VM's public IP. Wait a few minutes and retry.

Rate-limited by Let's Encrypt. Production issuance is limited to 50 certificates per registered domain per week. While you are iterating, use the staging environment:

sudo certbot --nginx -d nginx-ssl.example.com --staging --agree-tos -m admin@example.com --non-interactive

Staging certificates are not browser-trusted (the issuer chain shows (STAGING) Let's Encrypt), but they exercise the full issuance flow without touching the production rate limit. Remove them with sudo certbot delete --cert-name nginx-ssl.example.com before issuing a real one.

Renewal fails silently weeks later. Check the Certbot log:

sudo tail -100 /var/log/letsencrypt/letsencrypt.log

Step 11 — Everyday Operations

Reload the configuration without dropping connections:

sudo nginx -t && sudo systemctl reload nginx

Check error and access logs:

sudo tail -50 /var/log/nginx/error.log
sudo tail -50 /var/log/nginx/access.log

Stop, start, or restart the service:

sudo systemctl restart nginx

List all certificates managed by Certbot on this VM:

sudo certbot certificates

Support

cloudimg provides 24/7/365 expert technical support. Guaranteed response within 24 hours, one hour average for critical issues. Contact support@cloudimg.co.uk.