Overview

Date: 2026-05-11

Time: 15:35

Overview

This is a complete FTL2 deployment script that provisions a Linode cloud instance, hardens it, and deploys the catbeez-arcade web application — a collection of browser-based games served behind Caddy as a reverse proxy. It demonstrates FTL2 used as a full infrastructure-as-code tool: cloud provisioning, DNS configuration, security hardening, user management, and application deployment in a single async script.

Usage Patterns

The script is a standalone uv run script with inline dependency metadata — no separate install step needed:


# Set required secrets as environment variables
export LINODE_TOKEN="..."
export CLOUDFLARE_API_TOKEN="..."

# Run the deployment
uv run deploy.py

The core pattern is async with automation(...) as ftl: which creates a managed FTL2 session with state tracking and secret bindings. All operations flow through ftl — cloud module calls (ftl.community.general.linodev4), host-targeted operations (ftl["arcade"].shell(...)), and host management (ftl.addhost(...)).

Key invocation patterns:

API and Configuration

Secret bindings map module names to environment variables — FTL2 injects secrets automatically:


secret_bindings={
    "community.general.linode_v4": {"access_token": "LINODE_TOKEN"},
    "community.general.cloudflare_dns": {"api_token": "CLOUDFLARE_API_TOKEN"},
}

State file (state.json) enables idempotency — FTL2 tracks what has been provisioned.

Environment variables required:

Local file dependencies:

Key Behaviors

1. Two-phase host registration: Connects first as root to bootstrap an admin user, then re-registers the host as admin with ansible_become=True for sudo-based privilege escalation. This is a common FTL2 pattern for fresh instances.

2. Security-first ordering: All hardening (firewall, SSH lockdown, SELinux, crypto policies, fail2ban, service minimization) runs before any application deployment — so if hardening fails, no app content is exposed.

3. Firewall strategy: Uses firewalld's drop zone (silently drops all uninvited traffic) with explicit allowances for HTTP, HTTPS, and SSH from a specific IP range (136.56.0.0/16).

4. SELinux compatibility: Sets httpdcannetwork_connect boolean so Caddy can reverse-proxy to localhost:8000 under SELinux enforcing mode.

5. Idempotent-ish shell commands: Uses guards like which uv || (install uv) and grep -qF ... || echo ... to make shell commands safe to re-run, though heavy use of shell over dedicated modules reduces idempotency guarantees.

6. Uses a private Linode image (private/37121878) — Fedora 43 with Caddy pre-installed, avoiding runtime package installation.

Relationships