Multi-platformsecurityTested on real hardware

Generate a self-signed TLS certificate (Linux / macOS / Windows)

Create a self-signed TLS certificate and private key with OpenSSL for internal homelab services. Includes Subject Alternative Names so modern browsers accept it.

Shellbash
Updated
Script
bash
# Works on Linux, macOS, and Windows (with OpenSSL installed)
# Replace the values below with your own hostname, IP, and organisation

openssl req -x509 \
  -newkey rsa:4096 \
  -sha256 \
  -days 3650 \
  -nodes \
  -keyout server.key \
  -out server.crt \
  -subj "/CN=homelab.lan/O=Homelab/C=US" \
  -addext "subjectAltName=DNS:homelab.lan,DNS:*.homelab.lan,IP:192.168.1.1"

What this does

Generates two files in the current directory:

  • server.key — the RSA private key (keep this secret)
  • server.crt — the self-signed X.509 certificate (valid 10 years)

The -addext subjectAltName flag is critical — modern browsers (Chrome 58+, Firefox, Safari) reject certificates that only have a CN and no Subject Alternative Names (SAN). Without a SAN, you will get a NET::ERR_CERT_COMMON_NAME_INVALID error regardless of trusting the cert.

Prerequisites

OpenSSL is required on all platforms.

Linux: Usually pre-installed. If not:

sudo apt-get install -y openssl   # Ubuntu / Debian
sudo dnf install -y openssl       # Fedora / Rocky

macOS: Pre-installed. Verify: openssl version

Windows: Not built-in. Install via one of:

# Option 1: winget
winget install ShiningLight.OpenSSL

# Option 2: Chocolatey
choco install openssl

# Option 3: Git for Windows includes OpenSSL
# Use Git Bash to run the openssl command above

Verify: openssl version (should show 3.x)

Customise for your environment

Edit the -subj and -addext values before running:

Field Example Replace with
CN homelab.lan Your primary hostname
O Homelab Your name or org
C US Your 2-letter country code
DNS:homelab.lan homelab.lan Hostname(s) that will use this cert
DNS:*.homelab.lan *.homelab.lan Wildcard — covers all subdomains
IP:192.168.1.1 192.168.1.1 IP address of the service

Multiple SANs — add as many as needed, comma-separated:

-addext "subjectAltName=DNS:proxmox.homelab.lan,DNS:grafana.homelab.lan,IP:192.168.1.10,IP:192.168.1.68"

Inspect the certificate

# View the certificate contents (confirm SANs are present)
openssl x509 -in server.crt -text -noout | grep -A5 "Subject Alternative"

# Show expiry date
openssl x509 -in server.crt -noout -enddate

# Verify the key matches the certificate
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa  -noout -modulus -in server.key | openssl md5
# Both lines must produce the same hash

Trust the certificate on each client

Self-signed certs will show a browser warning until you add them to your system’s trust store.

Linux (Ubuntu / Debian):

sudo cp server.crt /usr/local/share/ca-certificates/homelab.crt
sudo update-ca-certificates

Linux (Fedora / Rocky / openSUSE):

sudo cp server.crt /etc/pki/ca-trust/source/anchors/homelab.crt
sudo update-ca-trust

macOS:

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain server.crt

Windows (PowerShell, run as Administrator):

Import-Certificate -FilePath "server.crt" `
  -CertStoreLocation Cert:\LocalMachine\Root

After adding to the trust store, restart your browser.

Install on common homelab services

Nginx:

server {
    listen 443 ssl;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
}

Proxmox (replaces the default self-signed cert):

cp server.crt /etc/pve/local/pve-ssl.pem
cp server.key /etc/pve/local/pve-ssl.key
systemctl restart pveproxy

Docker / Portainer: mount server.crt and server.key into the container and point the service’s TLS config at them.

Notes

  • -nodes means “no DES” — the private key is not encrypted with a passphrase. For a service certificate this is normal (the service needs to read the key at startup without human input). Store the key file with chmod 600 and restrict access
  • The 10-year validity (-days 3650) is practical for homelab use; production certificates should use 1 year or less
  • For a proper internal CA (sign multiple service certs from one trusted root), generate a CA key/cert separately and use openssl ca to sign — one root cert to trust across all clients instead of trusting each service cert individually