Skip to content

xeniosrahi/PaaS-deploy-action

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 

Repository files navigation

PaaS-deploy-action

A Github action for deploying PaaS on Edge / Home PCs

CapRover + Cloudflared + IPv6 + GPU/TPU + Tailscale (Tunneled by Default)

A batteries-included GitHub repo to bootstrap and operate a CapRover cluster on Ubuntu 24.04 with:

  • Cloudflare Tunnel (all apps tunneled by default)
  • Optional public IPv6 + Let’s Encrypt (direct AAAA)
  • Docker IPv6 with autogenerated ULA on all nodes
  • Tailscale (optional, exit-node capable)
  • GPU/TPU prep (NVIDIA, AMD, Coral) + node labels and placement rules
  • Turnkey Immich deployment (quota + extra mounts + GPU ML placement)
  • Per-app IPv6 network (ipvlan) and tunnel deny/allow control
  • Build on GitHub, Deploy from self-hosted to avoid tunnel upload limits
  • .env secrets masking during env sync
  • Rollout/Rollback workflow to pin images and revert quickly
  • Auto subdomain generation in CI (SUBDOMAIN_PREFIXprefix.apps.<root>), with input validation

📂 Repository Layout

.github/workflows/
├─ caprover-bootstrap.yml            # Manager bootstrap (tunneled + optional IPv6/LE)
├─ caprover-node-join.yml            # Join nodes: IPv6, GPU/TPU, Tailscale, auto-label
├─ caprover-node-labels.yml          # Manual labels
├─ immich-deploy.yml                 # Immich deploy (quota + mounts + GPU placement)
├─ caprover-app-placement-patch.yml  # Placement constraints (+ optional ipv6-public)
├─ cloudflared-tunnel-toggle.yml     # Per-FQDN tunnel deny/allow
├─ ipv6-pool-setup.yml               # Create ipv6-public (ipvlan) + ndppd
├─ app-build-and-deploy.yml          # Build on GitHub, deploy on self-hosted (auto subdomains)
├─ caprover-app-init-and-env.yml     # App init + healthcheck + env sync (denylist masking)
└─ caprover-rollout-rollback.yml     # Pin image (rollout) & revert (rollback)

apps/apex-welcome/
├─ Dockerfile
├─ index.html
└─ captain-definition

PROJECT_DESCRIPTION_FOR_LLM.md
README.md

🔑 Setup (once)

  1. Secrets (Repo → Settings → Secrets and variables → Actions)

    • Required: CAPROVER_PASSWORD, CF_TUNNEL_TOKEN
    • Optional: TAILSCALE_AUTHKEY, MANAGER_SSH_KEY, SSH_KNOWN_HOSTS
    • For non‑GHCR registries: REGISTRY_USERNAME, REGISTRY_PASSWORD
    • Convenience: ROOT_DOMAIN, APPS_WILDCARD_SUBDOMAIN (default apps), CAPTAIN_SUBDOMAIN (default captain)
  2. Bootstrap manager — run caprover-bootstrap.yml

    • Provisions CapRover and Cloudflared
    • Serves captain.<root> and *.apps.<root> through the tunnel by default
    • Optional: set EXPOSE_PUBLIC_IPV6=true to allow direct IPv6 + Let’s Encrypt
  3. Join workers — run caprover-node-join.yml per node

    • GPU_TYPE + APPLY_GPU_PREP=true if needed
    • Nodes auto‑labeled (e.g., gpu=nvidia)

🌐 Cloudflare One‑Time Setup (Wildcard)

These steps are one‑time per zone if you want any subdomain under *.apps.<root> to route via your tunnel.

A) DNS (Cloudflare Dashboard → DNS)

  • Add CNAME:
    • Name: *.apps
    • Target: <TUNNEL_UUID>.cfargotunnel.com
    • Proxy: Proxied (orange cloud)

You can also automate via cloudflared:

cloudflared tunnel route dns <tunnel-name> *.apps.yourdomain.com

B) Cloudflared Ingress (already baked by bootstrap)

/etc/cloudflared/config.yml should include:

ingress:
  - hostname: captain.yourdomain.com
    service: http://127.0.0.1:80
    originRequest: { httpHostHeader: captain.yourdomain.com }
  - hostname: "*.apps.yourdomain.com"
    service: http://127.0.0.1:80
  - service: http_status:404

Reload:

sudo systemctl restart cloudflared

C) CapRover

  • Nothing special: CapRover matches Host header and routes to the app.
  • In UI (or CI), add Custom Domain for the app (e.g., rahul.apps.yourdomain.com).

🚦 Optional: Direct IPv6 (BYO AAAA)

If you want certain apps to skip the tunnel:

  1. In DNS, add an AAAA:

    • Name: *.apps (or a specific subdomain)
    • IPv6: your server’s global IPv6
    • Proxy: DNS only (gray cloud)
  2. Ensure EXPOSE_PUBLIC_IPV6=true (bootstrap), so port 80/443 v6 are open and Let’s Encrypt is enabled.

  3. To force a given FQDN away from the tunnel, run cloudflared-tunnel-toggle.yml with ALLOW_TUNNEL=false for that hostname.

🚀 Deploy Flow

A) Initialize app + env

Use caprover-app-init-and-env.yml to create the app, set health checks, and sync .env from another repo.

  • Denylist masking (skipped keys):
    SECRET, _SECRET$, TOKEN, PASSWORD, PASS$, API_KEY, PRIVATE, CERT, AWS_, GCP_, AZURE_

B) Build on GitHub, deploy from self‑hosted

Use app-build-and-deploy.yml:

  • Inputs you care about

    • APP_NAME
    • REPO_URL, REPO_BRANCH, DOCKERFILE, CONTEXT
    • IMAGE_REGISTRY (default ghcr.io), IMAGE_NAMESPACE, IMAGE_NAME, IMAGE_TAG
    • SUBDOMAIN_PREFIX or CAPROVER_CUSTOM_DOMAIN
  • Auto subdomain

    • If CAPROVER_CUSTOM_DOMAIN is empty and you pass SUBDOMAIN_PREFIX, CI computes
      SUBDOMAIN_PREFIX + "." + (APPS_WILDCARD_SUBDOMAIN or "apps") + "." + ROOT_DOMAIN
    • Validation: prefix must match
      ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ (lowercase, digits, hyphens; 1–63 chars; no leading/trailing hyphen)
  • What happens

    • GitHub‑hosted runners build + push the image (multi‑arch supported)
    • Self‑hosted runner updates CapRover app to the image ref (origin pulls directly → avoids Cloudflare’s 100MB upload limit)

C) Placement constraints (optional)

Use caprover-app-placement-patch.yml to pin services to labeled nodes (e.g., gpu=nvidia), and optionally attach ipv6-public + fixed IPv6s.

D) Rollout / Rollback

Use caprover-rollout-rollback.yml:

  • Rollout: saves current image to PREV_IMAGE (changeable) and pins to IMAGE_REF.
  • Rollback: restores from PREV_IMAGE.

🧠 Keep in Mind

  • DNS Pre-req: wildcard CNAME for *.apps to the tunnel is a one‑time setup.
  • TLS at the Edge: with tunnel, Cloudflare terminates TLS. CapRover origin TLS (LE) applies to direct IPv6 and optional internal policies.
  • Rate Limits: Cloudflare has per‑request and bandwidth rules; container image pulls go direct from CapRover to your registry (not via the tunnel).
  • Let’s Encrypt Challenges: for direct IPv6, ensure ports 80/443 v6 are reachable; for tunneled hosts, Cloudflare handles edge certs.
  • Naming: subdomain prefixes must be DNS‑safe and unique. The validation step enforces safe characters/length.
  • Rollbacks: the workflow stores the previous image tag in an env var (PREV_IMAGE). Rotate or snapshot as needed for more slots.

🖼️ Immich One‑Click

  • Quota volume via IMMICH_STORAGE_GB
  • Extra mounts via IMMICH_EXTRA_MOUNTS
  • ML service constrained to GPU nodes with device reservation

🛠 Troubleshooting

  • Tunnel: systemctl status cloudflared, inspect /etc/cloudflared/config.yml
  • IPv6: ip -6 addr shows global v6; for per‑app v6, ensure routed /64 + ndppd
  • GPU: NVIDIA requires nvidia-container-toolkit; AMD/Coral prep included
  • CapRover: workflows are idempotent—re‑run safely

About

A Github action for deploying PaaS on Edge / Home PCs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors