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 operator accounts from access_users, 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.