Last active
October 29, 2025 14:49
-
-
Save kylefmohr/9ad8243e6641a9d0568a78cb9ae26ade to your computer and use it in GitHub Desktop.
Auto-renew Proxmox's Web UI HTTPS certificate using Tailscale and Tailnets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -e # Exit immediately if a command exits with a non-zero status. | |
| ## | |
| # This script automates renewing the Proxmox web UI TLS certificate | |
| # using Tailscale's built-in HTTPS certificate feature. | |
| # It is recommended to run this script using a cronjob, I'm using | |
| # `0 2 1 */3 * /path/to/my/prox-certs.sh` | |
| # in my crontab to run this every three months (the length of the certificate validity) | |
| # v4: Checks for and installs jq if missing. | |
| ## | |
| # Check for jq and install if it's missing | |
| if ! command -v jq &> /dev/null; then | |
| echo "jq could not be found. Installing it now..." | |
| apt-get update | |
| apt-get install -y jq | |
| fi | |
| # Get the node name (e.g., 'MyProxmoxHost1') | |
| NODENAME=$(hostname -s) | |
| if [ -z "$NODENAME" ]; then | |
| echo "Error: Could not determine hostname." >&2 | |
| exit 1 | |
| fi | |
| # Get the Tailscale tailnet name (e.g., 'marlin-pizza.ts.net') | |
| TAILNET_NAME=$(tailscale status --json | jq -r .CurrentTailnet.MagicDNSSuffix) | |
| if [ -z "$TAILNET_NAME" ]; then | |
| echo "Error: Could not determine Tailnet name. Is Tailscale running?" >&2 | |
| exit 1 | |
| fi | |
| # Construct the full domain name and define final paths | |
| FQDN="${NODENAME}.${TAILNET_NAME}" | |
| PVE_CERT_FILE="/etc/pve/nodes/${NODENAME}/pveproxy-ssl.pem" | |
| PVE_KEY_FILE="/etc/pve/nodes/${NODENAME}/pveproxy-ssl.key" | |
| # Create a temporary directory to avoid writing directly to pmxcfs | |
| TMP_DIR=$(mktemp -d) | |
| # Set a trap to automatically remove the temporary directory on script exit | |
| trap 'rm -rf -- "$TMP_DIR"' EXIT | |
| echo "Starting Proxmox certificate renewal for ${FQDN}..." | |
| echo "1. Requesting certificate from Tailscale into temporary directory..." | |
| tailscale cert \ | |
| --cert-file "${TMP_DIR}/cert.pem" \ | |
| --key-file "${TMP_DIR}/key.key" \ | |
| "${FQDN}" | |
| echo "2. Copying new certificate and key into place..." | |
| cp "${TMP_DIR}/cert.pem" "${PVE_CERT_FILE}" | |
| cp "${TMP_DIR}/key.key" "${PVE_KEY_FILE}" | |
| echo "3. Setting correct file permissions..." | |
| chown root:www-data "${PVE_KEY_FILE}" | |
| chmod 640 "${PVE_KEY_FILE}" | |
| chown root:www-data "${PVE_CERT_FILE}" | |
| chmod 640 "${PVE_CERT_FILE}" | |
| echo "4. Restarting pveproxy service to apply the new certificate..." | |
| systemctl restart pveproxy.service | |
| echo "Success! The Proxmox certificate for ${FQDN} has been renewed." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment