Skip to main content

Provisioning a New VPS

Step-by-step guide for bootstrapping a fresh EC2 instance using the Provision VPS GitHub Actions workflow.

diagram

Prerequisites

  • EC2 instance running Ubuntu 24.04 with a public IP
  • SSH access as your configured ssh_user (ubuntu for EC2 Ubuntu 24.04, root for most other providers)
  • All required GitHub Secrets set (see tables below)

Step 1 — Update VPS_IP

Go to Settings → Secrets and variables → Actions in GitHub and set:

VPS_IP = <new EC2 public IP>

Step 2 — Set up SSH access for GitHub Actions

The workflow needs to SSH into the new instance. Choose one option:

Paste the .pem file contents as the VPS_SSH_PRIVATE_KEY GitHub Secret:

cat ~/your-key.pem # paste the output into the secret

AWS automatically places the corresponding public key in ~ubuntu/.ssh/authorized_keys at launch — no manual step needed.

Lightsail — lost key pair recovery

Lightsail instances use a custom key pair set at creation time (LIGHTSAIL_KEY_PAIR_NAME). The private key can only be downloaded once — if it's lost, recover access via the Lightsail browser SSH console and add a new key manually.

1. Generate a new key pair locally:

ssh-keygen -t ed25519 -f ~/.ssh/ops4life -N ""

2. Open browser SSH — Lightsail console → instance → Connect using SSH

3. Authorise the new public key on the instance:

echo "$(cat ~/.ssh/ops4life.pub)" >> ~/.ssh/authorized_keys

4. Update ~/.ssh/config to use the new key:

Host <INSTANCE_IP>
HostName <INSTANCE_IP>
User ubuntu
IdentityFile ~/.ssh/ops4life
IdentitiesOnly yes

5. Test local access:

ssh ubuntu@<INSTANCE_IP>

6. Update VPS_SSH_PRIVATE_KEY GitHub Secret with the new private key:

cat ~/.ssh/ops4life # paste into the secret

Step 3 — Set all provision-time secrets

The following secrets are passed as Ansible extra vars. Set them all in Settings → Secrets and variables → Actions before running the workflow.

Always required

SecretDescription
VPS_SSH_PRIVATE_KEYPrivate SSH key for the runner to reach the VPS
VPS_IPPublic IP of the target EC2 instance
TRAEFIK_HOSTBase domain (e.g. ops4life.com)
ACME_EMAILEmail for Let's Encrypt certificate registration
GH_PATGitHub Personal Access Token — used for repo access and API calls
ANSIBLE_VAULT_PASSWORDPassword for ansible/secrets.yml

Core App Secrets

SecretUsed by
AUTHENTIK_SECRET_KEYauthentik
AUTHENTIK_PG_PASSauthentik (PostgreSQL password)
AUTHENTIK_BOOTSTRAP_PASSWORDauthentik
AUTHENTIK_BOOTSTRAP_EMAILauthentik
GH_OAUTH_CLIENT_IDauthentik
GH_OAUTH_CLIENT_SECRETauthentik
PG_N8N_PASSn8n
PG_PASSpostgres (global admin)
PGADMIN_PASSWORDpgadmin
GF_ADMIN_PASSWORDmonitoring (Grafana)
SMTP_PASSlistmonk (Resend/SMTP)

Step 4 — Run the Provision VPS workflow

Go to Actions → Provision VPS → Run workflow and accept the defaults:

InputDefaultNotes
ssh_userrootEC2 Ubuntu 24.04 AMIs default to ubuntu — change this input to ubuntu for EC2
verbosity-vIncrease to -vv or -vvv for more detail
tagsallSpecific tags to run (e.g. system,docker)

What the playbook does

  1. System: Installs packages (Node.js, Docker, Python, etc.) and configures swap.
  2. RDP: Sets up remote desktop with XFCE4 and Chrome.
  3. Repo: Clones vps-apps to /opt/vps-apps.
  4. Networks: Creates external Docker networks (traefik-net, postgres-net).
  5. Environment: Decrypts ansible/secrets.yml and writes .env files for all apps.
  6. Deploy: Orchestrates docker compose up -d for all services in dependency order.
  7. Configuration: Runs post-deploy scripts for Authentik, Listmonk, Kuma, etc.
  8. Sync: Syncs any dynamically generated secrets on the VPS back to ansible/secrets.yml.

Step 5 — Verify Deployment

Once the workflow finishes:

  1. Check the Uptime Kuma dashboard at https://kuma.<your-domain>.
  2. Verify Authentik is accessible at https://authentik.<your-domain>.
  3. Test SSO by logging into Guacamole or Grafana.