Adding a new server¶
This guide walks through the full process of provisioning a new server from scratch.
Overview¶
1. Add host to inventory
2. Create host variables (plain + vault)
3. Bootstrap from root
4. Apply full configuration
5. (Optional) Enable Zero Trust lockdown
Step 1 – Add the host to the inventory¶
Edit inventories/prod/hosts.yml and add the host under the appropriate group:
# Web server example
web:
hosts:
mysite-web-01:
ansible_host: mysite-e01.subscribed.build
# Database server example
db:
hosts:
mysite-db-01:
ansible_host: mysite-a01.subscribed.build
Step 2 – Create host variables¶
Create a directory for the host under host_vars/:
The easiest approach is to copy main.yml from a similar existing host of the same type and adjust it.
main.yml – non-sensitive variables¶
# inventories/prod/host_vars/mysite-web-01/main.yml
sites:
- name: mysite
domain: mysite.dk
user: mysite
project_path: /home/mysite/mysite-dk-bedrock
cronjob:
dir: mysite-dk
enable:
- name: as-global
- name: wp-cron
- name: as-payments-hub-15
- name: as-payments-hub-30
- name: as-payments-hub-45
system:
users:
- name: mysite
sudo: true
ssh:
generation: true
authorized_key: true
connect: true
cloudflare:
zero_trust:
slug_web: mysite-e01
application:
public_hostnames:
- subdomain: cms
domain: mysite.dk
path: /wp/wp-graphql
private_hostnames:
- subdomain: cms
domain: mysite.dk
path: '*'
vault.yml – encrypted secrets¶
Create and encrypt the vault file:
Add the required secrets (the exact keys depend on the host type):
# Web host vault example
vault_cloudflared_tunnel_token: ""
# DB host vault example
vault_mariadb_root_password: "a-strong-password"
sqlbak_db_user: "sqlbak"
sqlbak_db_password: "another-strong-password"
sqlbak_install_script_url: "https://..."
sqlbak_install_script_sha256: "sha256hash"
See inventories/prod/group_vars/db.vault.yml.example and web.vault.yml.example for the full list of required keys per server type.
Step 3 – Bootstrap from root¶
When a cloud provider gives you fresh root access, run the root bootstrap playbook. This creates the pt and sd operator accounts, installs their SSH keys, and configures passwordless sudo.
After this runs, you should be able to SSH as your own user:
Step 4 – Apply full configuration¶
Web server¶
# Option A: full web playbook (recommended)
ansible-playbook -i inventories/prod/hosts.yml web.yml --limit mysite-web-01
# Option B: bootstrap first (Docker + cloudflared), then full config
ansible-playbook -i inventories/prod/hosts.yml bootstrap_web.yml --limit mysite-web-01
ansible-playbook -i inventories/prod/hosts.yml web.yml --limit mysite-web-01
Database server¶
ansible-playbook -i inventories/prod/hosts.yml bootstrap_db.yml --limit mysite-db-01
ansible-playbook -i inventories/prod/hosts.yml db.yml --limit mysite-db-01
Step 5 – Enable Zero Trust lockdown (optional)¶
If the server should only be accessible via Cloudflare Tunnel (no public SSH):
- Verify cloudflared is running and the tunnel is healthy:
- Apply lockdown:
This closes all inbound UFW ports and binds SSH to localhost. After this, you can only reach the server through cloudflared access ssh.
- Update
main.ymlto reflect the locked-down state:
- Update your SSH config to use the proxy:
# ~/.ssh/config
Host mysite-e01.subscribed.build
User pt
IdentityFile ~/.ssh/id_ed25519
ProxyCommand cloudflared access ssh --hostname %h
Idempotency check¶
After the initial provisioning, run the playbook a second time and confirm no changes are applied:
If you see unexpected changes on the second run, investigate before pushing the configuration to other hosts.