Skip to main content
Version: 1.3

The fadebox CLI

fadebox is a single native binary that drives the same API the UI does, authenticating with an API key. It was built for pipelines — the useful half is not wrapping REST calls, it is the deploy loop, since deploy is asynchronous and anything that gates a build on an environment coming up has to poll, decide what counts as success, and report why it failed.

It works just as well from your own machine. Any key drives it, so you can point it at a key of your own and act as yourself, or at a service account's key and act as the pipeline does. If you sign in through SSO you have no password, so a personal key is how you use the CLI at all.

If what you want is an agent driving Fadebox rather than a command you type, that is the MCP endpoint instead.

Install​

Every Fadebox server hands out the CLI it was released with, so the download is your own installation, not a release page:

curl -fsSL -o fadebox https://fadebox.example.com/cli/fadebox-linux-amd64
chmod +x fadebox
sudo mv fadebox /usr/local/bin/fadebox

The download needs no sign-in — a pipeline's first step has nothing but the server's address, and that is enough — and it is always the server's own version: after an upgrade, download it again and fadebox --version matches the version in the sidebar footer. That footer links it too, as CLI beside the version, on every page. The binary's third-party notices sit beside it at /cli/fadebox-cli-THIRD-PARTY-NOTICES.txt, and the License page lists them with the server's own.

It is a native binary for Linux on amd64 with no runtime dependency — nothing to install alongside it. A development build of Fadebox carries no CLI and says so at that URL; a released image always does.

Connect​

login verifies a key against a server and stores it as a context:

fadebox login --url https://fadebox.example.com --project demo --env dev

It prompts for the key so it does not land in your shell history, calls /api/me to confirm it works, and writes ~/.config/fadebox/config.yaml — created with mode 0600 before anything is written to it, so the token never touches disk world-readable.

Whatever login prints back is who the CLI now acts as: your own username for a personal key, the service account's for a CI one. Every command afterwards reaches exactly as far as that account does.

Contexts are named, so one machine can talk to several installations:

fadebox context list
fadebox context use staging
fadebox context current

In CI, write no file at all. Every setting resolves flag → environment variable → context file:

FlagEnvironment variable
--urlFADEBOX_URL
--tokenFADEBOX_TOKEN
-p, --projectFADEBOX_PROJECT
-e, --envFADEBOX_ENV

FADEBOX_CONFIG_DIR relocates the context file if you do want one.

The deploy loop​

fadebox instance up pr-123 --wait --timeout 10m
  • up is find-or-create. If an instance with that slug exists it is reused and redeployed; otherwise it is created first. instance create stays strict by comparison — the API disambiguates a repeated name into pr-123-2 rather than failing, which is right for a UI and a quiet duplicate-per-retry for a pipeline.
  • --wait polls status and renders per-container state and health as they come up. It exits 0 on RUNNING and 1 on ERROR, printing the failure reason the UI would show — otherwise only reachable by having a human open the UI. RUNNING is as strong as the template's healthchecks: a service that declares one counts as ready when it turns healthy, one without the moment it starts — so on a healthcheck-less stack --wait can return before the app accepts a connection.
  • --timeout (default 10m) and --poll-interval (default 2s) bound the wait. They accept 90s, 10m and the like.
  • On success it prints the instance's URLs, taken from the deployed containers.
  • --runtime applies on create only — an existing instance keeps its runtime until instance move changes it, which needs the instance DRAFT or STOPPED. Left out, the server picks local when it is enabled and available to the project, otherwise the project's only enabled runtime, and answers 409 runtime_unavailable with the candidates when several qualify — name one then. instance list and get mark a runtime that has since been disabled as (disabled): the instance cannot deploy there, see Runtime.
  • Deploying re-arms the instance's lifetime. A branch that keeps getting pushed keeps its environment alive; one that goes quiet is stopped once the expiry runs out, and instance get prints when that is.
  • instance list has a HEALTH column beside STATUS, and instance get prints a Health line, carrying what the daemon last said — status is what fadebox did, health is what is running now. A pipeline can gate on it: RUNNING means the deploy finished, HEALTHY means it is still up.
  • --purpose and --link say what the copy is for — the branch, the merge request, the ticket — so the instance list reads as something other than a wall of slugs. Both work on create and up, --link is repeatable, and they stick to the instance across every redeploy. instance list shows the purpose in a PURPOSE column, cut at 40 characters.
  • --shared and --personal say whose the copy is: every deployer's, or the creator's and the editors' alone. Both work on create and up, on create only — an existing instance keeps its sharing, and up says so when the flag disagrees — and left out, the environment's own default applies. instance share and instance unshare change it afterwards, which is the creator's or an editor's to do. instance list shows it in a SHARING column and instance get prints Shared: no (personal to alice); a stop or delete the account may not make prints the server's refusal, which names whom to ask.
  • --print-token mints the ingress cookie for an instance on a runtime that requires a sign-in, so the suite can open the URL the deploy just printed. It implies --wait and adds an accessToken key to the status document; against an ungated runtime the key is there with an empty token list, so the step needs no condition around it.

What an instance runs is its environment's definition, read fresh on every deploy — an instance carries no parameters of its own. To deploy other versions, set them on the environment first (env service set), or clone the environment into a second version stream (env clone).

A blueprint environment refuses instances altogether — instance create and up answer 409. env list marks one in its KIND column and env get prints a Blueprint: line, so a pipeline that must not assume can check before it deploys. Cloning a blueprint yields a deployable environment, so env clone base dev then instance up is the way through.

--wait is available on up, deploy and stop.

Command surface​

fadebox loginVerify a key and store a context.
fadebox context list | use | currentSwitch between installations.
fadebox env list | getEnvironments of the project.
fadebox env clone <source> <slug>Copy an environment's definition — the second version stream, and how a blueprint is used.
fadebox env service set <name> --image-tag …Set what a service runs: image tag, env overrides, job flag. A full replace — options left out are cleared (except --job).
fadebox instance list | get | create | up | deploy | stop | deleteThe instance lifecycle.
fadebox instance move <slug> --runtime <runtime>Move a DRAFT or STOPPED instance to another runtime. Its volumes do not follow and the new runtime has its own ingress domain, so the next deploy comes up on new URLs and empty — see Runtime.
fadebox instance reset <slug> --yesWipe the instance's data — containers, network and volumes — and deploy it again with the same overrides. --yes is required when stdin is not a terminal.
fadebox instance set <slug> --var … --tag … [--redeploy]Set this instance's values on the environment's open services. A full replace: send every override it should end up with, or --clear to remove them all.
fadebox instance describe <slug> --purpose … --link …Say what this copy is for: one line and up to eight labelled links. Pass only one of the two and the other is carried over; --clear removes both.
fadebox instance share | unshare <slug>Make an instance every deployer's, or its creator's alone — see Ownership. The creator's or an editor's to do.
fadebox instance check <slug> <name> --state passed|failed|pendingReport a named check on what the instance is running now — --summary for the line, --run-url for the job, --remove to delete it.
fadebox instance approve | reject <slug> --note …A person's verdict on what is running. A rejection must carry the note.
fadebox instance verdict <slug> [--withdraw]Print the verdict that stands, or take it back.
fadebox instance token <slug>Mint the ingress cookie for an instance behind Require fadebox sign-in — one value per hostname, valid 12 hours. An ungated runtime answers with none.
fadebox instance status | logsLive state and a log snapshot — for a stopped instance, the log captured at the stop, with a note on stderr.
fadebox template list | getTemplates visible to the key's account.
fadebox runtime listRuntimes; narrowed to those the project may deploy onto. CPU and MEMORY show what each host is spending against its capacity, as of the last reading — – where nothing has been observed yet.

create and up take the same --var <service>:<KEY>=<value> and --tag <service>=<tag> flags, both repeatable, so an instance can be made with its values in one call:

fadebox instance up pr-1842 --tag backend=2.7.0-rc3 --var backend:LOGGING_LEVEL_ROOT=DEBUG --wait

Whether a service accepts either is the server's decision, so a closed one fails fast with the 400 it would give anyone. Setting values does not deploy — add --redeploy to set to follow through.

Every command reaches exactly as far as the key's account does — a 403 means a missing role, exactly as it would for a person.

A 401 means the server would not accept the token at all. The CLI cannot tell you which reason, because the server deliberately gives the same answer to every one of them — a wrong secret, a key that was revoked or has expired, a deactivated account, and an account being made to change its password all come back identically, so the endpoint cannot be used to probe which keys exist. The things worth checking, in the order they usually bite: has the key expired (its expiry is on the key's row in the UI), was it revoked, and can the account still sign in at all.

Output and exit codes​

-o table (default), -o json and -o yaml. Every command emits one document, so jq reaches any single field of a result. A table renders timestamps in your shell's own time zone; json and yaml carry them as the API does, as instants with the offset spelled out (2026-08-16T09:30:00Z). A status document keys its containers by service, so one service is an address rather than a search:

fadebox instance status pr-123 -o json | jq -r '.containers.web.urls[0]' # one service's URL
fadebox instance status pr-123 -o json | jq -r '.containers[].urls[]' # every URL
fadebox instance status pr-123 -o json | jq -r '.containers | keys_unsorted[]' # the services

A service has one URL per ingress port, so urls is a list even for a single service. Beside containers, a status document carries checks (keyed by check name), verdict and references — the facts the card shows about what is running — so a job can read back what it reported without a second call. instance get prints the same as Purpose:, Links:, Checks:, References: and Verdict: lines.

--wait writes its live container progress to stderr, and only the final result to stdout. So URL=$(fadebox instance up pr-123 --wait -o json | jq -r '.containers.web.urls[0]') captures the URL alone while you still watch the deploy happen in the job log.

Exit codeMeaning
0Success.
1The operation failed — a deploy that ended in ERROR, or any 4xx/5xx.
2Usage error.

Conflicts arrive as plain text and are printed as such: "Instance is RUNNING — stop it before deleting" is the message, not the status line.

A pipeline​

export FADEBOX_URL=https://fadebox.example.com
export FADEBOX_TOKEN=$CI_FADEBOX_KEY # from your CI secret store
export FADEBOX_PROJECT=demo FADEBOX_ENV=dev

# the CLI comes from the server it is about to drive: the same version, no key needed
curl -fsSL -o /usr/local/bin/fadebox "$FADEBOX_URL/cli/fadebox-linux-amd64"
chmod +x /usr/local/bin/fadebox

# pin this build's images on the environment, then bring the instance up and wait for it
fadebox env service set api --image-tag "$CI_COMMIT_SHA"
fadebox env service set web --image-tag "$CI_COMMIT_SHA"

I="pr-$CI_MERGE_REQUEST_IID"

# say what the copy is for, wait for it, and take the ingress cookie in the same step
fadebox instance up "$I" --wait --print-token -o json \
--purpose "$CI_MERGE_REQUEST_TITLE" \
--link "MR !$CI_MERGE_REQUEST_IID=$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID" \
> deploy.json

URL=$(jq -r '.containers.web.urls[0]' deploy.json)
echo "Preview: $URL"

# … run the suite against $URL; where the runtime gates it, the test runner sets
# .accessToken.cookie to the matching .accessToken.tokens[].value on that host …

fadebox instance check "$I" e2e --state passed \
--summary "84 passed, 0 failed" --run-url "$CI_JOB_URL"

If the deploy fails, instance up --wait exits 1 with the reason and the pipeline stops there. Report --state failed from the job's failure path and the instance carries the bad news on its own card, where whoever opens the merge request will look.

The teardown — fadebox instance stop "$I" --wait then fadebox instance delete "$I" — belongs on the merge or close event rather than at the end of this job. The purpose, the check and the verdict are only worth recording on a preview somebody can still open; a stop three seconds after the suite passes leaves nothing to review. What keeps that from accumulating copies is the expiry, not the pipeline.

Note that the image tags land on the environment, so every instance of dev follows them — right for one version stream per environment. Streams that must not share versions are separate environments (fadebox env clone dev dev-v2, target with FADEBOX_ENV). Editing an environment is a maintainer act, so a pipeline that pins versions needs its key issued with that role — or no write access at all: with a git value source as the tag, the env service set calls disappear entirely, the deploy reads the version from your values file, and the default deployer key suffices.

Not yet​

  • logs is a snapshot, not a stream. -f would mean polling and de-duplicating a tail window, which is wrong at the edges; it waits for a streaming endpoint rather than being faked.
  • No apply. Pushing a directory of template and environment manifests into an installation is designed but unbuilt; today the CLI drives instances and edits environments — it does not create templates or environments from scratch.
  • Linux amd64 only as a published binary.