For Agents

fk-dev is the second VM in the levandor-infra Terraform root, provisioned on 2026-06-30 alongside the multi-VM refactor. It hosts the FaceKom dev mirror. The FaceKom team manages docker compose themselves via the vuer_docker repo — this infra repo only provides the base (Docker + monitoring + Tailscale). Read this entire note before touching fk-dev.

Required reading for fk-dev

If you’re an agent that has been asked to do anything touching fk-dev, read this note first and the spec-1-multi-vm-refactor before issuing any commands.

Identity

PropertyValue
GCE namefk-dev
GCP projectaerobic-tesla-490112-r3
Region / zoneeurope-west6 / europe-west6-a
Machine typee2-standard-4 (4 vCPU / 16 GB RAM)
Boot disk100 GB pd-balanced
External IP34.158.19.122 (egress only — firewall denies all inbound)
Tailscale IP100.91.108.61
Tailscale MagicDNSfk-dev.taild4189d.ts.net
Tailscale tagtag:cloud (NOT a new tag:dev — see decision below)
Service accountfk-dev-sa@aerobic-tesla-490112-r3.iam.gserviceaccount.com
VPCfk-dev-net (custom-mode, isolated from pmv2-zurich’s VPC)
Subnetfk-dev-subnet (10.2.0.0/24)
Inventory groupfk_dev_base (in ansible/site.yml)

Specs rationale

  • e2-standard-4 (4 vCPU / 16 GB): dev mirror for the FaceKom vuer_* services — needs headroom for vuer_oss + vuer_css + vuer_cv + auxiliary services running concurrently. pmv2-zurich’s e2-small would not be enough.
  • 100 GB pd-balanced: FaceKom docker images are larger than pmv2’s (ML models, OCR pipelines). 100 GB gives room for 9+ services with image layers without churning.
  • europe-west6-a (Zurich): kept in Switzerland for data-residency parity with the FaceKom production environment.

Network isolation

fk-dev lives in its own VPC (fk-dev-net) and subnet (10.2.0.0/24), separate from pmv2-zurich’s network. Two reasons:

  1. Independent firewall scoping — fk-dev’s needs (dev work, port forwarding for testing) are different from pmv2-zurich’s (production-tight).
  2. Easier cleanup — if fk-dev gets decommissioned, the VPC tears down with it cleanly.

Tailscale spans both VPCs at the application layer — operators reach either VM via MagicDNS.

Tailnet identity

AspectValue
Tailnettaild4189d
Magic DNSfk-dev.taild4189d.ts.net
Tagtag:cloud
Auth keyReuses the existing pmv2 auth key (reusable + ephemeral + pre-authorized)

Decision: tag:cloud, NOT tag:dev

The provisioning agent suggested creating a new tag:dev for fk-dev to scope ACLs more tightly. Andras explicitly overrode this — tag:cloud is reused for simplicity. Same auth key, same ACL entry, fewer moving parts. If a future operator needs differentiated ACL rules for dev vs prod nodes, revisit this decision before adding more dev VMs.

The shared tag:cloud means fk-dev appears in the same Tailscale SSH ACL rule that already governs pmv2-zurich:

{ "action": "accept", "src": ["autogroup:member"], "dst": ["tag:cloud"], "users": ["ops", "root"] }

Firewall posture

  • Inbound: denied (default). Only the tailscale_direct rule allows udp:41641 for Tailscale NAT traversal.
  • Outbound: unrestricted (apt updates, Docker image pulls from europe-west3-docker.pkg.dev, GitHub, Tailscale DERP).
  • No public SSH port — same Tailscale-only access model as pmv2-zurich.

Roles applied

fk-dev is in the fk_dev_base inventory group, which receives these roles via ansible/site.yml:

RolePurposeNotes
baseapt update/upgrade, fail2ban, unattended-upgrades, ops user, sudoersIdentical to pmv2-zurich.
dockerDocker CE 29.6.1, compose plugin, gcloud apt repo, AR credential helper for europe-west3Cred helper points at the existing apps AR repo; fk-dev MAY pull pmv2 images for cross-service testing if needed, but typically pulls FaceKom-owned images.
github_keys(skipped)No local deploy key — intentional for a dev box. FaceKom team will SSH-forward credentials or use HTTPS clones with PATs as needed.
monitoringOTel collector → ingest.eu2.signoz.cloud:443 + tailscaled Prometheus scrape on 127.0.0.1:5252Same SigNoz Cloud EU2 tenant as pmv2-zurich. Host metrics + tailscaled metrics ship.

Roles NOT applied

RoleWhy excluded
appspmv2-specific. fk-dev’s docker compose is operator-managed via the FaceKom vuer_docker repo, NOT via deploys/apps.yml.
babylonBabylon (Rust MCP coord) is a pmv2-fleet service. fk-dev doesn’t participate in pmv2 agent coordination.
natsNATS is the pmv2 event bus. fk-dev has no need to join it.
bsc_exploitpmv2-specific BSC exploit watcher. Irrelevant to FaceKom dev work.

The exclusion is structural — fk-dev is in the fk_dev_base host group, and the pmv2_full play in site.yml (which loads apps + babylon + nats + bsc_exploit) doesn’t target it.

Babylon GCS write access

fk-dev’s service account is NOT in the babylon_backup_vm_writer IAM binding. The for_each on that resource filters to VMs whose ansible_groups contains pmv2_full. fk-dev’s groups are ["fk_dev_base"] — explicitly excluded.

resource "google_storage_bucket_iam_member" "babylon_backup_vm_writer" {
  for_each = {
    for k, v in var.vms : k => v
    if contains(v.ansible_groups, "pmv2_full")
  }
  # …
}

This is sensible scoping: a dev box has no business writing to the babylon backup bucket.

FaceKom team layering plan

The FaceKom team will deploy 9 services on top of the base provided by this repo, using the 8b multi-tailscaled sidecar pattern:

  • Each service gets its own userspace tailscaled container.
  • Each service gets its own MagicDNS name (e.g. vuer-oss-dev.taild4189d.ts.net).
  • Each service gets its own tailscale cert for TLS termination.

Reference patterns (managed by THIS repo, useful as templates):

  • pmv2-zurich’s babylon sidecar — see babylon-deploy-notes for the 3-service compose (babylon + litestream + babylon-tailscale).
  • pmv2-zurich’s nats sidecar — same Tailscale sidecar pattern.

Boundary: who owns what

This repo (levandor/terraform) provisions and configures fk-dev’s base: VM, Docker, Tailscale node identity, OTel collector. After make provision, the Ansible run stops there.

The FaceKom vuer_docker repo owns everything on top: docker compose, per-service tailscaled sidecars, per-service certs, per-service env files. Operators do NOT add fk-dev services to deploys/apps.yml.

How to reach fk-dev

# SSH (uses Tailscale SSH, no keypair needed)
make ssh VM=fk-dev
# or directly:
ssh ops@fk-dev.taild4189d.ts.net
# or by Tailscale IP:
ssh ops@100.91.108.61

The Makefile defaults VM=pmv2-zurich for backwards compatibility — always pass VM=fk-dev for fk-dev work.

How to re-run Ansible on fk-dev

make configure                            # runs ALL plays (both pmv2-zurich and fk-dev)
# or limit:
ansible-playbook -i ansible/inventory/hosts.ini ansible/site.yml --limit fk-dev

The site.yml split (per-host-group plays) means a make configure won’t accidentally apply pmv2-only roles to fk-dev.

Tailscaled restart drops the SSH session

The monitoring role’s “Restart tailscaled” handler will drop the ansible SSH session when fk-dev is being managed over tailnet SSH. Ansible reports unreachable=1 for fk-dev. The handler does succeed — re-run make configure and the second run will show changed=0, confirming convergence. See [[gcp-terraform-ansible-gotchas#39|gotcha #39]].

Cost estimate

Rough monthly steady-state:

  • e2-standard-4 sustained-use: ~$98/mo
  • 100 GB pd-balanced: ~$10/mo
  • Ephemeral external IP: ~$3.65/mo
  • Egress: variable, but dev box is low-traffic — assume ~$5/mo

**Total: ~18/mo at e2-small) but appropriate for the workload — 4 vCPU / 16 GB is what 9 concurrent services on a dev box require.

What NOT to do

  • Don’t add fk-dev services to deploys/apps.yml — that manifest is pmv2-fleet only. FaceKom team owns their own compose setup.
  • Don’t create a tag:dev without revisiting the decision above with Andras.
  • Don’t grant fk-dev’s SA write access to babylon backup bucket — the for_each filter is correct as-is.
  • Don’t make configure without checking which play limits you want — by default it runs every play. Use --limit fk-dev to scope.
  • Don’t hand-configure docker compose on the VM — that’s FaceKom’s territory via vuer_docker.