freenginx on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of freenginx on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. freenginx is a community-driven fork of the nginx HTTP server, reverse proxy and load balancer, maintained by Maxim Dounin. It keeps nginx's configuration language and behaviour, so everything you already know about nginx.conf, server blocks and location blocks applies unchanged.
The image ships freenginx 1.30.1 (the current stable line) compiled from the official source tarball with SSL, HTTP/2, real-IP, gzip-static, stub-status and sub-filter modules, and hardened compiler flags. Rather than a bare default, it boots with a production configuration: the server version is hidden (server_tokens off), only TLS 1.2 and 1.3 are accepted, HTTP/2 is on, gzip is enabled and common security headers are set. HTTPS works out of the box because each VM generates its own self-signed certificate, private key and Diffie-Hellman parameters at first boot — nothing secret is baked into the image.
What is included:
-
freenginx 1.30.1 compiled from source to
/usr/sbin/nginx, running as thenginxsystem user -
Hardened
/etc/nginx/nginx.conf:server_tokens off, gzip, TLS 1.2/1.3, session hardening -
Default site at
/etc/nginx/conf.d/default.confserving the cloudimg landing page on TCP 80 and TCP 443 -
Document root at
/var/www/html -
Per-VM self-signed TLS certificate, key and DH parameters generated at first boot in
/etc/nginx/ssl/(never shipped in the image) -
Local-only
stub_statushealth endpoint at/nginx_status(bound to127.0.0.1) -
Commented reverse-proxy
locationblock ready for you to point at an upstream -
nginx.serviceandfreenginx-firstboot.servicesystemd units auto-starting on boot -
Listeners: TCP 80 (HTTP), TCP 443 (HTTP/2 + TLS)
-
Ubuntu 24.04 LTS base with latest security patches applied at build time and unattended-upgrades enabled
-
Azure Linux Agent for seamless cloud integration and SSH key injection
-
24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
-
An active Azure subscription
-
A subscription to the freenginx on Ubuntu 24.04 listing on Azure Marketplace
-
An SSH public key for VM authentication
-
A virtual network and subnet in the target region
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for typical web and reverse-proxy traffic. freenginx is efficient with a small memory footprint — scale up only for very high request rates or large reverse-proxy fleets.
Step 1: Deploy from the Azure Portal
Navigate to Marketplace in the Azure Portal, search for freenginx, select the cloudimg publisher entry, and click Create.
On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range, and TCP 80 and TCP 443 from the client networks that should reach your site.
Click Review + create, wait for validation, then Create. Deployment takes around two minutes.
Step 2: Deploy from the Azure CLI
RG="freenginx-prod"
LOCATION="eastus"
VM_NAME="freenginx-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/freenginx-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
--resource-group "$RG" \
--name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s \
--admin-username "$ADMIN_USER" \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
Open the web ports on the VM's network security group (adjust the source ranges to your own networks):
az network nsg rule create --resource-group "$RG" --nsg-name "${VM_NAME}NSG" \
--name allow-http --priority 1001 --destination-port-ranges 80 --access Allow --protocol Tcp
az network nsg rule create --resource-group "$RG" --nsg-name "${VM_NAME}NSG" \
--name allow-https --priority 1002 --destination-port-ranges 443 --access Allow --protocol Tcp
Step 3: Verify the service is running
SSH to the VM and confirm freenginx is active, enabled at boot, and listening on TCP 80 and 443. The self-signed certificate and DH parameters are generated once at first boot by freenginx-firstboot.service.
systemctl status nginx --no-pager
nginx -v
ss -tln | grep -E ':80 |:443 '
Expected output:
● nginx.service - freenginx - high performance web server (cloudimg)
Loaded: loaded (/etc/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running)
nginx version: freenginx/1.30.1
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*

Step 4: Review the build configuration
The image is compiled from the official freenginx source tarball. Inspect the exact modules and compiler flags it was built with, and confirm the running configuration passes a syntax test:
nginx -V
nginx -t
The build includes the SSL, HTTP/2, real-IP, stub-status, gzip-static and sub-filter modules, with stack-protector and RELRO hardening flags. nginx -t reports the configuration syntax is OK.

Step 5: Confirm the hardened defaults and TLS
Check that the server version is hidden, the security headers are present, the per-VM self-signed certificate is in place, and HTTP/2 is negotiated on TCP 443:
curl -sI http://localhost/ | grep -iE '^HTTP|^Server:|^X-|^Referrer'
echo | openssl s_client -connect 127.0.0.1:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates
curl -skI --http2 https://localhost/ | grep -iE '^HTTP|^strict-transport'
Server: freenginx appears with no version number (server_tokens off), the X-Content-Type-Options, X-Frame-Options and Referrer-Policy headers are set, the certificate is this VM's own self-signed cert, and HTTP/2 200 confirms HTTP/2 is active with HSTS on HTTPS.

The default landing page is served from /var/www/html over both HTTP and HTTPS:

Step 6: Serve your own website
Drop your files into the document root, or edit the default site. The document root is /var/www/html:
ls -la /var/www/html/
cat /etc/nginx/conf.d/default.conf
To serve your own static site, replace the contents of /var/www/html, then test and reload:
sudo nginx -t
sudo systemctl reload nginx
Step 7: Reverse-proxy an upstream application
freenginx makes an excellent reverse proxy and load balancer. The default site ships a commented location /app/ block — uncomment it in /etc/nginx/conf.d/default.conf and point proxy_pass at your backend:
location /app/ {
proxy_pass http://127.0.0.1:8080/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
To load-balance across several backends, declare an upstream block and proxy to it:
upstream app_pool {
server 10.0.0.4:8080;
server 10.0.0.5:8080;
}
server {
listen 80;
location / {
proxy_pass http://app_pool;
}
}
After editing any configuration, always validate before reloading — a reload never drops live connections:
sudo nginx -t
sudo systemctl reload nginx
Step 8: Install a real TLS certificate
The image ships with a per-VM self-signed certificate so HTTPS works immediately. To serve a certificate browsers trust, replace the two files referenced by the default :443 server block and reload:
/etc/nginx/ssl/cloudimg.crt— your certificate (full chain)/etc/nginx/ssl/cloudimg.key— your private key
ssl_certificate /etc/nginx/ssl/cloudimg.crt;
ssl_certificate_key /etc/nginx/ssl/cloudimg.key;
Copy your certificate and key over those paths (keeping the key readable only by root), then:
sudo nginx -t
sudo systemctl reload nginx
For automated Let's Encrypt certificates, install certbot with the nginx plugin (sudo apt-get install -y python3-certbot-nginx) and run sudo certbot --nginx -d your-domain.example — Certbot rewrites the certificate paths for you and installs a renewal timer.
Security notes
-
No baked secret. The TLS private key, certificate and DH parameters are generated uniquely on every VM at first boot in
/etc/nginx/ssl/; the private key is mode0600 root:root. No two instances share a key, and nothing secret ships in the image. -
Version hidden.
server_tokens offmeans responses carry a bareServer: freenginxheader with no version, reducing fingerprinting. -
TLS 1.2 and 1.3 only, with session tickets disabled and HSTS set on HTTPS responses.
-
Local-only status. The
stub_statusmetrics endpoint at/nginx_statusis restricted to127.0.0.1. -
Patched at build time with unattended-upgrades left enabled, so the VM keeps receiving OS security updates.
Support
- Documentation and guides: cloudimg.co.uk
- Email support: support@cloudimg.co.uk (24/7, 24 hour response SLA)
- freenginx project: freenginx.org