fusionaly-deploy

Use ONLY when explicitly invoked. Installs Fusionaly on a server you can reach over SSH with a key. Optionally provisions a new Hetzner server or sets up Cloudflare DNS.

6 stars

Best use case

fusionaly-deploy is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use ONLY when explicitly invoked. Installs Fusionaly on a server you can reach over SSH with a key. Optionally provisions a new Hetzner server or sets up Cloudflare DNS.

Teams using fusionaly-deploy should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/fusionaly-deploy/SKILL.md --create-dirs "https://raw.githubusercontent.com/karloscodes/fusionaly-oss/main/.claude/skills/fusionaly-deploy/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/fusionaly-deploy/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How fusionaly-deploy Compares

Feature / Agentfusionaly-deployStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use ONLY when explicitly invoked. Installs Fusionaly on a server you can reach over SSH with a key. Optionally provisions a new Hetzner server or sets up Cloudflare DNS.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Deploy Fusionaly

Install Fusionaly on a Linux server you can reach over **SSH with a key**. The standard path: connect to a box you already have, run the installer, point your domain. Provisioning a new Hetzner server and configuring Cloudflare DNS are **optional extras** — offer them, don't assume them.

**Principles:**
- **Key-based SSH only.** Connect with the user's existing SSH key / `ssh-agent`. Never ask for, type, or store a password — that's both the secure default and what lets the agent operate on the box cleanly. If a server only allows password auth, help set up a key first (`ssh-copy-id`); do not proceed over a password.
- **Existing server is the default.** Any box reachable by SSH key works (Hetzner, DigitalOcean, Linode, EC2, a home server). Hetzner provisioning and Cloudflare DNS are opt-in conveniences, not the path.
- **Avoid "analytics" in the domain.** Ad/privacy blockers (uBlock Origin, EasyPrivacy) block hostnames containing `analytics`, `tracking`, `stats`, `telemetry` — visitors' requests get dropped before they ever reach the server. When asking for the domain, steer the user to a neutral subdomain (e.g. `data.example.com` or a brandable name), never `analytics.example.com`.
- **Scan first, report status, ask before every invasive action.**

## Flow

1. Scan environment (silent)
2. Show status report
3. Fix missing tools (ask for each)
4. Choose: new Hetzner server or existing server
5. Gather details (domain, location, SSH key)
6. Cloudflare DNS? (optional)
7. Confirm plan
8. Create server (Hetzner only)
9. Verify install URLs are reachable
10. Harden server (ask first)
11. Install Fusionaly (ask first)
12. Verify install worked
13. Create DNS record (ask first)
14. Summary + reminders

## Phase 1: Preflight

### Step 1 — Scan Environment (silent, no questions)

Run all checks, collect results:

```bash
# hcloud CLI
which hcloud && hcloud version
hcloud context active 2>/dev/null

# flarectl
which flarectl

# Local SSH keys
ls ~/.ssh/*.pub 2>/dev/null

# Hetzner SSH keys (only if hcloud configured)
hcloud ssh-key list 2>/dev/null
```

### Step 2 — Present Status Report

Show everything at once so the user sees the full picture:

```
Environment check:
  hcloud CLI:       ✓ installed (v1.x.x) / ✗ not installed
  Hetzner token:    ✓ configured (context: xxx) / ✗ not configured
  flarectl:         ✓ installed / ✗ not installed
  Local SSH keys:   ✓ N keys found / ✗ none found
  Hetzner SSH keys: ✓ N keys / — (can't check without token)
```

### Step 3 — Fix Missing Tools (ask for each)

For each missing item, ask whether to install/configure. One at a time.

**hcloud CLI** (only needed for new server creation):
```bash
brew install hcloud
```

**Hetzner API token** (only needed for new server creation). Walk the user through:

```
You need a Hetzner Cloud API token with read/write permissions.

  1. Go to https://console.hetzner.cloud
  2. Select your project (or create one)
  3. Go to Security → API Tokens
  4. Click "Generate API Token"
  5. Name it (e.g. "fusionaly-deploy"), select Read & Write
  6. Copy the token (you won't see it again)
```

Then store it:
```bash
hcloud context create fusionaly --token <token-user-provided>
# Verify it works
hcloud server-type list > /dev/null && echo "Hetzner token works"
```

**SSH keys** — if none exist:
```bash
ssh-keygen -t ed25519
```

**flarectl** — handled later in Cloudflare section if user opts in.

### Step 3b — Choose Mode

Default to the existing-server path. Only offer provisioning if they don't have a box.

```
Do you already have a server you can SSH into with a key?
  • Yes → use it (any provider). [default]
  • No  → I can provision one on Hetzner for you (optional).
```

## Phase 2: Gather Choices

Ask one question at a time. Wait for answer before asking next.

### Path A: New Hetzner Server

**If user declined hcloud install in step 3:** explain it's required for this path and stop gracefully.

#### Domain

```
What domain will Fusionaly run on? (e.g. analytics.example.com)
```

#### Server Location

Fetch from `hcloud location list` and present the list. Let user pick.

#### Server Type

Fetch from `hcloud server-type list`, filter to shared CPU types (cx/cax), and present with specs and prices. Let user pick.

Fusionaly minimum: 1 vCPU, 512MB RAM, 10GB SSD.

#### SSH Key

```bash
hcloud ssh-key list
```

If keys exist in Hetzner: let user pick from list.
If no Hetzner keys but local keys exist: offer to upload one.

Upload with: `hcloud ssh-key create --name <name> --public-key-from-file <path>`

### Path B: Existing Server

Ask:
1. **Server IP:** "What's your server's IP address?"
2. **SSH user:** "What SSH user should I use? (e.g. root, ubuntu, admin)"
3. **SSH port:** "What SSH port? (default: 22)"
4. **Domain:** "What domain will Fusionaly run on?"

Verify SSH connectivity:
```bash
ssh -o ConnectTimeout=5 -p <port> <user>@<ip> echo ok
```

If it fails, help troubleshoot (wrong key, port, user).

### Cloudflare (optional, both paths)

```
Do you use Cloudflare for DNS on this domain? (y/n)
```

If yes:

**1. Install flarectl** (if not already installed):
```bash
brew install cloudflare/cloudflare/flarectl
```

**2. Get a Cloudflare API token.** Walk the user through this:

```
You need a Cloudflare API token with DNS edit permissions.

  1. Go to https://dash.cloudflare.com/profile/api-tokens
  2. Click "Create Token"
  3. Use the "Edit zone DNS" template
  4. Under Zone Resources: select the zone for your domain
  5. Click "Continue to summary" → "Create Token"
  6. Copy the token (you won't see it again)
```

**3. Store and verify the token.** Ask the user to paste it, then:

```bash
# Export for this session
export CF_API_TOKEN="<token-user-provided>"

# Verify it works — this MUST succeed before proceeding
flarectl zone list
```

If `flarectl zone list` fails (401, no zones, etc.) — stop and troubleshoot. Do NOT continue to DNS setup with a broken token.

## Phase 3: Confirm & Create

### Confirmation

Show full plan. Adapt based on path:

**New Hetzner server:**
```
Ready to provision:
  Server:     <type> (<specs>)
  Location:   <location-name> (<location-id>)
  Image:      Ubuntu 24.04
  SSH key:    <key-name>
  Domain:     <domain>
  Cloudflare: yes / no

Proceed? This will create a billable server.
```

**Existing server:**
```
Ready to deploy:
  Server:     <ip> (via <user>@<ip>:<port>)
  Domain:     <domain>
  Cloudflare: yes / no

Proceed?
```

**Only proceed on explicit yes.**

### Create Server (Hetzner path only)

Sanitize domain for server name: replace dots with dashes, lowercase (e.g. `analytics.example.com` → `fusionaly-analytics-example-com`).

```bash
hcloud server create \
  --name fusionaly-<sanitized-domain> \
  --type <type> \
  --location <location> \
  --image ubuntu-24.04 \
  --ssh-key <key-name>
```

Capture the public IP from output. Wait for SSH to become available:

```bash
# Poll until SSH is ready (max ~60 seconds)
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@<ip> echo ok
```

Tell the user: "Server created at `<ip>`. SSH is ready."

## Phase 4: Server Setup

### Verify Install URLs Are Reachable

Before telling the user to SSH in, check the URLs work:

```bash
curl --head --silent --fail https://raw.githubusercontent.com/karloscodes/server-hardener/main/harden.sh > /dev/null && echo "server-hardener: reachable" || echo "server-hardener: UNREACHABLE"
curl --head --silent --fail https://fusionaly.com/install > /dev/null && echo "fusionaly installer: reachable" || echo "fusionaly installer: UNREACHABLE"
```

If either URL is unreachable, tell the user before proceeding.

### Harden Server (ask first)

```
Should I run the server-hardener? This will:
  - Create admin user with SSH key
  - Disable root login and password auth
  - Configure firewall (80/443 open)
  - Option for Tailscale (locks SSH to Tailscale only)
  - Enable unattended security upgrades
```

If yes, the hardener is interactive — the user must run it themselves:

```
The server-hardener is interactive (4 questions). Run this in your terminal:

  ssh root@<ip> 'curl -fsSL https://raw.githubusercontent.com/karloscodes/server-hardener/main/harden.sh -o /tmp/harden.sh && sudo bash /tmp/harden.sh'
```

**Wait for user to confirm hardening is complete before proceeding.**

After hardening, ask what SSH access method they now have:
- Tailscale mode: `ssh admin@<tailscale-ip>`
- No Tailscale: `ssh -p 2222 admin@<ip>`

### Install Fusionaly (ask first)

```
Should I install Fusionaly now? This will:
  - Install Docker
  - Set up Caddy reverse proxy with SSL
  - Configure automatic backups and updates
```

If yes, the installer is interactive — the user must run it themselves:

```
The Fusionaly installer is interactive (asks for domain). Run this in your terminal:

  ssh <user>@<host> 'curl -fsSL https://fusionaly.com/install | sudo bash'
```

Use the SSH access from the hardening step (admin user, correct port).

**Wait for user to confirm installation is complete.**

### Verify Install Worked

After the user confirms installation is complete, verify it's actually running:

```bash
# Wait a moment for SSL to provision, then check
curl --silent --max-time 10 -o /dev/null -w "%{http_code}" https://<domain>
```

If you get a 200 or 302: install confirmed.
If connection refused or timeout: SSL may still be provisioning (Let's Encrypt needs DNS to resolve first). Tell the user to wait a few minutes and check `https://<domain>` in their browser.

### Cloudflare DNS (if opted in, ask first)

```
Should I create the A record now?
  <domain> → <server-ip>
```

If yes — `CF_API_TOKEN` should already be exported from the Cloudflare setup step earlier:

```bash
# CF_API_TOKEN was exported during Cloudflare setup in Phase 2
flarectl dns create \
  --zone <base-domain> \
  --name <subdomain> \
  --type A \
  --content <server-ip>
```

Where `<base-domain>` is extracted from the domain (e.g. `example.com` from `analytics.example.com`) and `<subdomain>` is the prefix (e.g. `analytics`).

If `CF_API_TOKEN` is not set (e.g. new shell session), re-export it:
```bash
export CF_API_TOKEN="<token-from-earlier>"
```

## Phase 5: Summary

Adapt based on what was actually done. Only show completed steps.

**New Hetzner server:**
```
Done! Here's what was set up:

  Server:      fusionaly-<domain> (<ip>)
  Location:    <location-name> (<location-id>)
  Type:        <type> (<specs>)
  OS:          Ubuntu 24.04
  Hardened:    ✓ / ✗ skipped
  Tailscale:   ✓ / ✗ / — (skipped hardening)
  Fusionaly:   ✓ installed at <domain> / ✗ skipped
  DNS:         ✓ A record via Cloudflare / manual setup needed
  SSH access:  ssh admin@<tailscale-ip> / ssh -p 2222 admin@<ip>
  Dashboard:   https://<domain>/admin
```

**Existing server:**
```
Done! Here's what was set up:

  Server:      <ip> (via <user>@<ip>:<port>)
  Hardened:    ✓ / ✗ skipped
  Fusionaly:   ✓ installed at <domain> / ✗ skipped
  DNS:         ✓ A record via Cloudflare / manual setup needed
  SSH access:  <the access method from hardening>
  Dashboard:   https://<domain>/admin
```

### Conditional Reminders

Only show what's relevant:

- **If no Cloudflare:** "Create an A record pointing `<domain>` to `<ip>`. SSL via Let's Encrypt won't activate until DNS propagates."
- **If Tailscale chosen:** "Approve this server in your Tailscale admin console (https://login.tailscale.com/admin)."
- **If hardening skipped:** "Your server is NOT hardened. Root SSH is still open. Strongly recommend running the server-hardener."
- **If Fusionaly skipped:** "Fusionaly is not installed yet. SSH in and run the installer when ready."
- **Always (if Fusionaly installed):** "Auto-updates run daily at 3 AM. Backups stored at `/opt/fusionaly/storage/backups/`."
- **Always (if Fusionaly installed):** "Consider off-server backups: VPS snapshots, rsync, or Litestream to S3."
- **If Cloudflare was used:** "Your Cloudflare token is set for this session only. To persist it, add to `~/.zshrc`: `export CF_API_TOKEN=\"your-token\"`"

Related Skills

fusionaly

6
from karloscodes/fusionaly-oss

Use when user asks about website analytics, traffic, visitors, page views, referrers, or mentions "fusionaly". Queries Fusionaly analytics via SQL API.

tech

6
from karloscodes/fusionaly-oss

Use when writing or reviewing Go code in fusionaly-oss — adding routes, handlers, domain contexts, background jobs, the manager CLI, wiring the app, or writing tests. Covers the cartridge framework, matcha for the manager, Phoenix Contexts, lifecycle/shutdown, and the test conventions.

qa

6
from karloscodes/fusionaly-oss

Use after code changes, before releases, or when testing features - runs the right level of QA based on what changed

design

6
from karloscodes/fusionaly-oss

Use when styling Fusionaly UI components, pages, or charts - applies the Fusionaly design system with black/white palette and brand accents

deployment-patterns

144923
from affaan-m/everything-claude-code

Deployment workflows, CI/CD pipeline patterns, Docker containerization, health checks, rollback strategies, and production readiness checklists for web applications. Use when setting up deployment infrastructure or planning releases.

DevelopmentClaude

makepad-deployment

31392
from sickn33/antigravity-awesome-skills

CRITICAL: Use for Makepad packaging and deployment. Triggers on: deploy, package, APK, IPA, 打包, 部署, cargo-packager, cargo-makepad, WASM, Android, iOS, distribution, installer, .deb, .dmg, .nsis, GitHub Actions, CI, action, marketplace

Developer ToolsClaude

expo-deployment

31392
from sickn33/antigravity-awesome-skills

Deploy Expo apps to production

Mobile DevelopmentClaude

devops-deploy

31392
from sickn33/antigravity-awesome-skills

DevOps e deploy de aplicacoes — Docker, CI/CD com GitHub Actions, AWS Lambda, SAM, Terraform, infraestrutura como codigo e monitoramento.

DevOps & InfrastructureClaudeCursorGemini

deployment-validation-config-validate

31392
from sickn33/antigravity-awesome-skills

You are a configuration management expert specializing in validating, testing, and ensuring the correctness of application configurations. Create comprehensive validation schemas, implement configurat

DevOps ToolsClaude

deployment-procedures

31392
from sickn33/antigravity-awesome-skills

Production deployment principles and decision-making. Safe deployment workflows, rollback strategies, and verification. Teaches thinking, not scripts.

DevOps & InfrastructureClaude

deployment-pipeline-design

31392
from sickn33/antigravity-awesome-skills

Architecture patterns for multi-stage CI/CD pipelines with approval gates and deployment strategies.

DevOps & InfrastructureClaude

deployment-engineer

31392
from sickn33/antigravity-awesome-skills

Expert deployment engineer specializing in modern CI/CD pipelines, GitOps workflows, and advanced deployment automation.

DevOps & Cloud InfrastructureClaude