Web local upload uses the multipart upload endpoint; CLI local deploy creates the archive for you.
CLI, import, billing, and route handoff
Operate Fugue without guessing what the route, runtime, or bill will do next.
Fugue is route-first: start on a shared managed runtime, keep the public route stable, and move the same app onto your own machine later. This page focuses on the parts that are easiest to misread from code alone: CLI defaults, import boundaries, fugue.yaml, billing activation, and the line between stateless migrate and managed failover.
This is the current default free managed envelope before paid managed capacity starts.
The default price book is calibrated so this managed envelope lands near four dollars per month.
Read this before the first import
These are the edges users usually trip over on the first real deploy.
- 01CLI default runtime is shared managed.
Omit--runtimeand Fugue targets the shared managed runtime. - 02Seed-file overrides are topology-preserving only.
persistent_storage_seed_filesonly works when topology detection stays fully automatic. - 03Move and failover are separate paths.
migratehandles stateless apps andmovable_rwopersistent storage; failover is for declared standby recovery.
CLI
CLI quick start and operator defaults
The CLI is the main operator interface. It covers deploy, import, inspect, runtime, failover, and raw HTTP diagnostics without dropping into kubectl.
For normal deploys, one tenant-scoped access key is enough. Reserve platform-admin or bootstrap keys for fugue admin ..., cross-tenant inspection, or product-layer debugging.
For self-hosted control planes, set both FUGUE_BASE_URL and FUGUE_WEB_BASE_URL. The first points CLI HTTP requests at the API, the second is needed when commands read fugue-web snapshot routes.
Secrets are intentionally redacted from fugue app overview and fugue operation show JSON output. Only add --show-secrets when you explicitly need raw values during debugging.
Shell environment
- FUGUE_API_KEY
- Current access keyRequired for normal deploys and tenant-scoped operations.
- FUGUE_BASE_URL
- Self-hosted API base URLSet this on self-hosted control planes so CLI requests hit the right API origin.
- FUGUE_WEB_BASE_URL
- Self-hosted web base URLNeeded by admin snapshot and
fugue web diagnosecommands that talk tofugue-webroutes. - FUGUE_SKIP_UPDATE_CHECK
- Optional quiet modeSuppresses the release-upgrade reminder in the current shell session.
High-signal commands
| Goal | Command | Why it matters |
|---|---|---|
| Deploy current source | fugue deploy . | Uploads local source as an archive and queues an import without inventing extra scaffolding. |
| Inspect full app context | fugue app overview my-app | Shows domains, bindings, image inventory, operations, and rollout context in one snapshot. |
| Tail runtime logs | fugue app logs runtime my-app --follow | Follows pod logs without leaving the CLI. |
| Query log-style tables | fugue app logs query my-app --table gateway_request_logs --since 1h --match status=500 | Useful when the useful data lives in the app database instead of stdout. |
| Call internal HTTP safely | fugue app request my-app GET /admin/requests --header-from-env X-Service-Key=SERVICE_KEY | Injects auth headers from app env so you do not paste service secrets into the shell. |
| Check and apply updates | fugue version --check-latest / fugue upgrade | Released binaries can self-upgrade in place. |
curl -fsSL https://raw.githubusercontent.com/yym68686/fugue/main/scripts/install_fugue_cli.sh | sh
export FUGUE_API_KEY=<copied-access-key>
fugue deploy .
fugue app overview my-appexport FUGUE_BASE_URL=https://api.example.com
export FUGUE_WEB_BASE_URL=https://app.example.com
export FUGUE_API_KEY=<copied-access-key>
fugue app ls
fugue version --check-latest
fugue upgradeImports
Import paths, flags, and topology guardrails
Fugue supports three import surfaces today: local source, GitHub repositories, and image references. GitHub and local uploads can also fan out a topology-aware manifest into multiple apps and operations.
GitHub import is no longer limited to public static sites. The current importer supports public and private repos, tracked repository sync, and stack-aware imports from either fugue.yaml or Compose.
Image imports stay the thinnest path: point Fugue at an image reference, optionally name the app, and skip build detection entirely.
If a local archive name implies the app name clearly, upload import can derive it automatically. Accepted archive formats are .zip, .tgz, and .tar.gz.
Import guardrails
- GitHub imports support public and private repositories, automatic build detection (
auto,static-site,dockerfile,buildpacks,nixpacks), and service-specific.envoverrides with--service-env-file <service>=<path>. - Local source on the CLI is archived automatically. In the web product layer, local upload is post-sign-in and the JSON create-and-import route deliberately rejects
local-upload; that path must use the multipart upload endpoint. - Topology-preserving seed-file overrides only work when the importer can keep build detection fully automatic. If you set
source_dir,dockerfile_path, orbuild_context_dir, Fugue rejectspersistent_storage_seed_files. - When
network_mode=background, Fugue clears service ports and public routes. Whennetwork_mode=internal, the service port remains, but the public route stays empty.
Flags that change behavior materially
--runtimedefaults to the shared managed runtime when omitted.--portor--service-portonly matters for public or internal services.--commandbecomessh -lc <command>on the queued app spec and queued operation.--storage-size,--mount, and--mount-filedefine persistent workspace state that later changes howmigrateand failover behave.--managed-postgresand the--postgres-*flags attach app-owned managed PostgreSQL from the start instead of treating it as an afterthought.
Import paths
| Path | Best for | Key parameters | Watch out for |
|---|---|---|---|
| Local source | fugue deploy . or multipart upload from the web app | --name, --build, --command, persistent-storage flags | Web local upload is multipart-only and happens after sign-in. |
| GitHub repository | fugue deploy github owner/repo | --branch, --private, --repo-token, --service-env-file | Private repos need explicit repo auth unless the workspace already has GitHub access wired. |
| Container image | fugue deploy image ghcr.io/example/app:latest | --name, --runtime, --replicas, --command | No build detection runs here, so Dockerfile/source-dir flags are irrelevant. |
Network modes
| Network mode | Route behavior | Port behavior | Typical use |
|---|---|---|---|
| Public | Public route is created | Service port is exposed | Default HTTP app path |
| Internal | No public route | Internal service remains addressable | Service-to-service traffic only |
| Background | No public route | Service ports are cleared | Workers, schedulers, queue consumers |
fugue deploy .
fugue deploy github owner/repo --branch main
fugue deploy github owner/repo --private --repo-token $GITHUB_TOKEN
fugue deploy github owner/repo \
--service-env-file gateway=.env.gateway \
--service-env-file runtime=.env.runtime
fugue deploy image ghcr.io/example/app:latest --name demo
fugue deploy . --backgroundfugue.yaml
fugue.yaml topology rules and template metadata
fugue.yaml is the clearest way to tell Fugue about services, dependencies, bindings, template metadata, and backing services without relying on inference alone.
Compose import is still supported, but fugue.yaml gives you a better place to name the primary service, declare reusable template variables, and describe backing-service ownership directly.
The importer treats app services, managed Postgres declarations, backing services, and bindings as one topology. That is why fugue.yaml matters for both deploy correctness and later failover behavior.
Rules Fugue enforces
- If more than one service is marked public, set
primary_serviceexplicitly. Fugue rejects ambiguous multi-public topologies. - If exactly one service is public, Fugue can infer
primary_servicefor you. - Missing local files referenced from
volumesbecome editablepersistent_storage_seed_files, which is how the importer surfaces required file mounts before the first deploy. persistent_storage.storage_sizeandstorage_class_namemerge with mounts inferred from volumes instead of replacing them.template.variablescan mark values asrequired,secret, andgenerate=password, which is useful when the repo is intended to behave like a reusable starter.
Top-level keys
| Root field | Meaning | Current behavior |
|---|---|---|
version | Manifest schema version | Current examples use version: 1. |
primary_service | Public entrypoint for the topology | Required when more than one service is public; otherwise Fugue can infer it if only one public service exists. |
template | Starter metadata | Supports template name, slug, docs/demo URLs, default runtime, and variable definitions. |
services | Deployable app and managed service topology | Used to derive app imports, compose dependencies, bindings, and seed files. |
backing_services | Shared attached services like Postgres or Redis | Useful when you want explicit ownership and bindings instead of raw env strings. |
Service-level fields
| Service field | Scope | What it influences |
|---|---|---|
public / port | Per service | Controls public-route eligibility and the internal/public port map. |
build.strategy, build.context, build.dockerfile | Per service | Pins build detection and file paths when auto detection is not enough. |
env / environment | Per service | Static environment baked into the imported service spec. |
depends_on | Per service | Feeds controller-side dependency ordering for topology deploys. |
bindings | Per service | Connects app services to named backing services. |
volumes and persistent_storage | Per service | Drive persistent mount imports, storage class/size, and editable seed-file prompts. |
version: 1
primary_service: web
template:
name: Next starter
slug: next-starter
description: Ship a ready-made Next.js app.
source_mode: github
default_runtime: runtime_edge_hk
variables:
- key: NEXT_PUBLIC_SITE_URL
label: Site URL
description: Public app URL.
default: https://example.com
required: true
- key: SESSION_SECRET
label: Session secret
description: Used to sign cookies.
generate: password
secret: true
services:
web:
public: true
port: 3000
build:
strategy: dockerfile
context: .
dockerfile: Dockerfile
env:
API_BASE_URL: http://api:8000/v1
depends_on:
- api
volumes:
- ./api.yaml:/workspace/api.yaml
persistent_storage:
storage_size: 10Gi
storage_class_name: fast-rwo
api:
port: 8000
build:
strategy: dockerfile
context: apps/api
dockerfile: Dockerfile
bindings:
- db
backing_services:
db:
type: postgres
image: postgres:17.6-alpine
owner_service: apiBilling
Billing activation, defaults, and why image inventory matters
Billing is envelope-based for managed capacity and event-based for paid public runtimes. The important distinction is that billing activation is broader than “the app is currently handling traffic.”
Live usage and committed billing are different views. The billing API can skip live managed-usage aggregation and return committed billing data only, which is useful when you care about the saved envelope and current billable posture rather than a live probe.
Managed image inventory matters. Retained managed images contribute to billing activation because managed storage is part of the envelope, even if the app itself is otherwise quiet.
Default resource baselines
- Free managed cap
- 500m CPU · 512 MiB · 5 GiBCurrent default free managed envelope per tenant.
- Default managed app
- 250m CPU · 512 MiB · 0 GiB extra storageDefault managed app resources before you override them.
- Default managed Postgres
- 500m CPU · 1024 MiB · 1 GiB storageDefault app-owned managed Postgres footprint.
- Default managed workspace
- 10 GiB storageWorkspace storage is billable storage when enabled.
What users usually underestimate
- Billing stays inactive until managed capacity, retained managed image inventory, or paid public runtime usage becomes active.
- Once billing is active for managed capacity, Fugue meters the saved managed envelope hourly from your balance, not just the latest instantaneous pod reading.
- A consumer deploying onto a paid public runtime is billed hourly, while the runtime owner receives the matching credit event.
- Your own attached servers stay free unless you publish them for other tenants as paid public runtimes.
- The self-serve web top-up flow currently accepts whole USD amounts from 5 to 5000.
Billing status meanings
| Status | Meaning | Typical next action |
|---|---|---|
| Inactive | No managed resource, retained managed image inventory, or paid public runtime usage is currently billable. | No action needed unless you are expecting managed capacity to be active. |
| Active | Managed envelope or paid public runtime usage is deducting balance hourly. | Check envelope size, current usage, and image inventory if the rate looks surprising. |
| Restricted | Balance is depleted while there is still billable managed/public runtime activity. | Top up before increasing capacity or deploying onto paid public servers. |
| Over cap | Current live managed capacity is above the saved managed envelope. | Save a higher cap before adding more managed capacity. |
fugue admin billing show
fugue admin billing cap --cpu 2000 --memory 4096 --storage 30
fugue admin billing topup 25 --note "manual credit"
fugue project images usage marketingContinuity
Stateless migrate versus managed failover
Fugue intentionally splits stateless migrate from managed failover. Users usually confuse those two paths when a project graduates from shared runtime to attached runtime or needs stateful recovery planning.
fugue app move and /v1/apps/{id}/migrate are the active move path. They can move stateless apps and persistent storage that is explicitly migratable, such as movable_rwo.
Legacy workspace storage or dedicated PVC storage must be converted to a migratable mode before an app move.
Managed failover is controller-orchestrated. It can fence the app, wait for final workspace sync, switch runtimes, and restore replicas when failover targets have been declared.
Audit states
ready: no hard stateful blocker is attached and stateless posture looks reasonable.caution: Fugue does not see a hard blocker, but replicas or runtime posture still need work.blocked: stateless migration is intentionally blocked because managed backing services or persistent workspace state are still attached.
Migrate vs failover
| Path | Use when | Hard boundary |
|---|---|---|
| Migrate | You are actively moving the app to another runtime. | Blocked when persistent storage is still legacy workspace or dedicated PVC. |
| Failover | You need controller-orchestrated runtime switch and state-aware recovery. | Needs declared failover targets and is strongest in same-control-plane environments. |
fugue app continuity audit
fugue app continuity audit my-app
fugue app failover policy set my-app --app-to runtime-b --db-to runtime-c
fugue app failover run my-app --to runtime-bDiagnose
Inspect, diagnose, and trust the contract
The shortest path to the right answer is usually an inspect or diagnose command, not an SSH session. Fugue already exposes topology, route, log, runtime, and HTTP timing probes directly in the CLI.
Use template inspection before importing unfamiliar repositories. That path shows primary_service, warnings, template metadata, and any editable persistent seed files without mutating anything.
Use fugue api request and fugue diagnose timing when you need to prove whether a problem is contract shape, control-plane latency, or an app-level behavior mismatch.
High-value details
fugue app fscan browse either persistent storage roots or the live container filesystem; choose--source persistentor--source livedeliberately.fugue app request --header-from-envis safer than pasting service secrets into your terminal history.fugue runtime doctor sharedfolds location-specific managed-shared runtime IDs into the shared view, so the output maps to real backing nodes during debugging.- The control plane publishes
/openapi.yaml,/openapi.json, and/docs; that contract is the HTTP source of truth.
Debugging shortcuts
| Command | Best use | Why it beats guessing |
|---|---|---|
fugue template inspect github owner/repo | Check a repo before import | Shows topology, template metadata, warnings, and seed files before the first mutation. |
fugue app overview my-app | Read current app posture | Pulls route, domains, bindings, operations, images, and rollout context together. |
fugue app logs pods my-app | See current and recent pod groups | Useful after rollouts when the current runtime log tail no longer explains what changed. |
fugue app logs query ... | Interrogate business-log tables | Avoids writing raw SQL every time a request-log table is the real source of truth. |
fugue runtime doctor shared | Inspect runtime health and attachment | Checks status, endpoint, recent heartbeat, and matching nodes in one view. |
fugue api request GET /v1/apps | Raw control-plane API debugging | Shows status, headers, body, server-timing, and transport timings directly. |
fugue diagnose timing -- app overview my-app | Latency attribution | Reports DNS, connect, TLS, TTFB, and total timing per HTTP request inside a normal CLI command. |
fugue template inspect github owner/repo
fugue deploy inspect github owner/repo
fugue app logs pods my-app
fugue app logs query my-app --table gateway_request_logs --since 1h --match status=500
fugue app request my-app GET /admin/requests --header-from-env X-Service-Key=SERVICE_KEY
fugue app fs ls my-app / --source live
fugue runtime doctor shared
fugue api request GET /v1/apps
fugue diagnose timing -- app overview my-app