User guide
Flyghtdeck Docs
Flyghtdeck runs your shell scripts on machines you set up once, with the variables, secrets and run history kept beside them. This guide covers everything in the app.
- QuickstartFrom sign-in to a first run.
- ScriptsWrite, parameterise and run.
- Variables and secretsValues a run is given.
- RunnersThe machines scripts run on.
Quickstart
A first run needs three things: a workspace to hold the work, a runner to run it on, and a script. The first time you sign in, a short setup sheet offers to make all three for you. To do it by hand:
-
Create a workspace. Open the workspace switcher in the top bar and choose + New workspace. Name it after what it runs, for example
Deployments. -
Build a runner. Open Runners, press + and pick Templates. Choose a template, keep pressing Next, then Create runner. The box is built once and kept.
-
Add a script. Open Scripts, press + and name it
hello. On the Editor tab replace the starter body and press Save:echo "Hello from Flyghtdeck" -
Run it. On the Console tab press Run. The log streams as the script works:
14:02:11 [Flightdeck] INFO ./hello started 14:02:11 Hello from Flyghtdeck 14:02:11 [Flightdeck] INFO ./hello exited 0 after 0s
From here, give the script parameters, keep its credentials as secrets, and invite the people who should run it.
How it fits together
| Thing | What it is |
|---|---|
| Organisation | The sharing boundary. It owns workspaces and has members; groups decide what each member can see and do. Every account starts with a personal organisation. |
| Workspace | The scripts, environments and runners for one job of work. |
| Script | A shell script with optional parameters, run on demand. |
| Environment | A named set of variables and secrets, such as dev or production. A run uses one. |
| Runner | A persistent Linux box, built once from a setup script, that runs execute on. |
| Run | One execution of a script: its parameters, log, status and exit code. |
The app has three pages in its left rail (a bottom bar on phones): Scripts, Environments and Runners. Each shows the current workspace. Settings, including organisations, are under the account menu at the top right.
Scripts and folders
Press + at the top of the Scripts sidebar and type a name. The
name is a path: slashes make folders, so deploys/api/release creates a script
called release inside deploys/api/. A path must be unique in the
workspace, ignoring case.
Folders are only the paths of the scripts in them. There is no empty folder: one appears when a script is created, renamed or moved into it, and goes when its last script leaves. Each folder row has its own + to add a script there.
Row actions
| Action | What it does |
|---|---|
| Run | Starts a run. Becomes Cancel or Stop while one is in flight. |
| Rename | Changes the name. A slash in the new name also moves the script. Not available while it is running. |
| Duplicate | Copies the body and parameters into the same folder as name copy. |
| Move | Sets the destination folder. Use / for the root. |
| Copy name, Copy path | Copies release or /deploys/api/release. |
| Delete | Deletes the script. |
A folder's menu has Rename, which moves every script in it, and Delete, which deletes every script inside it.
Finding scripts
- Search matches script names, not folder names.
- The sidebar's ⋯ menu has Collapse all or Expand all, Go to current, and a sort by Date added or Alphabetical.
- The arrow keys move through the tree; Enter opens a script or toggles a folder.
- The dot beside a name shows the last run: queued, running, succeeded, failed or cancelled.
Working with others
If someone else changes a script you have open, the sidebar shows Conflict and Run is disabled for that script. Press the refresh icon beside it to load the current version.
Editing a script
A selected script has four tabs.
| Tab | What is there |
|---|---|
| Console | The environment and runner pickers, the parameter fields, Run, and the live log. |
| Editor | The script body and its parameter definitions. |
| History | Past runs with their logs and parameters. |
| Settings | Require confirmation to run, and Delete. |
Saving is explicit. Changes to the body and the parameters are one draft, marked by a dot on the Editor tab, until you press Save. Revert discards the draft and Compare shows the draft beside the saved version. A draft survives moving between pages but not a browser refresh.
Scripts run with bash
Every script is run by bash, whatever its first line says. To run
Python, Node or anything else, call it from the script:
python3 tool.py.
Parameters
Parameters are the inputs a run is started with. Define them on the Editor tab under Parameters; each one reaches the script as an environment variable of the same name.
| Type | In the Console | Default |
|---|---|---|
| text | A text field. | Optional. The field starts with it. |
| boolean | A checkbox, sent as true or false. | false or true. |
| enum | A list to pick from. | The first option. Options are comma-separated. |
- Names use letters, digits and
_, and must not start with a digit, because they become environment variables. - Required text parameters block a run until they have a value. They are
marked with a red
*in the Console. A checkbox is always true or false, so it cannot be required. - Values are kept on the run and shown in History to anyone who can read it. Do not type a credential into a parameter; keep it as a secret.
Referencing a variable
A parameter value can point at a variable with {{NAME}}, either typed in the
Console or set as the parameter's default. Typing {{ opens a list of the names
available. The reference is resolved when the run starts, against the organisation's
variables and the chosen environment's.
REGION text default {{AWS_REGION}}
TARGET enum options {{STAGING_HOST}}, {{PRODUCTION_HOST}}
Hover a reference to see what it resolves to; a secret shows as ****. A
reference that matches nothing is tinted red and reaches the script as the literal text.
History keeps the reference, never the resolved value.
A reference is only needed to pass a variable under a different name or to choose between several. Every variable already reaches the script under its own name.
Running a script
The Console header chooses where a run goes.
- Environment starts on the workspace's default. Picking another changes it for this script until you reload; it does not change the default.
- Runner starts on the workspace's default runner. Organisation runners are listed above the workspace's own.
Press Run. While a run is in flight the button turns red and reads Cancel if the run is still queued or Stop once it is executing. Stop ends the process straight away, with no confirmation.
- One run per script. A script cannot be started again while it has a run queued or running. Different scripts can run on the same runner at once.
- Waiting runs queue. When your plan's concurrent runs are all in use, a new run waits its turn. A run that waits more than an hour is not started.
- The Queue. The bar at the bottom of the app shows how many runs are running and waiting across the organisation. Open it to see each one, its place in the queue, and a button to stop it.
- Time limit. A run is killed when it reaches its runner's run timeout, an hour unless changed.
Require confirmation to run
For a script that could do damage if started by accident, turn on Require confirmation to run on its Settings tab. Run then opens a slider that has to be dragged across before the run starts. With the keyboard, move the handle to the end with the arrow keys and press Enter.
Logs
The Console streams a run's output as it happens. Each line has the time it was written;
hover it for the full timestamp. Lines the platform writes itself start with
[Flightdeck] and are coloured by level. Everything else is your script's
output, shown as it was printed.
09:30:02 [Flightdeck] INFO ./release started
09:30:03 flyght: Checking out main from https://github.com/acme/api.git into api
09:30:05 flyght: Checked out main at 4f2a91c Bump version
09:31:40 BUILD SUCCESS
09:31:40 [Flightdeck] INFO ./release exited 0 after 1m 38s- The buttons above the log copy it, download it as
name.log, and jump to the top or bottom. - The Console shows the last 5,000 lines of a long log. Download saves all of it.
- Secret values are replaced with
****in the log. See Secrets for the limits of that.
History and statuses
The History tab lists the script's 50 most recent runs, newest first, with the status, run number, environment, duration and exit code. Open a finished run for its Logs and the Parameters it was started with. A run that is still going opens in the Console.
| Status | Meaning |
|---|---|
| Queued | Waiting for a free slot. |
| Running | Executing on the runner. |
| Succeeded | The script exited 0. |
| Failed | The script exited with another code, or could not be started. |
| Timed out | The run reached the runner's time limit and was killed. |
| Cancelled | Someone cancelled or stopped it. |
A finished run can be deleted from its row, which removes the record and its log. The address bar always points at what you are looking at, so a script, a tab or a single run can be shared as a link with anyone who has access.
Inside a run
What a script can rely on when it starts:
- Shell.
bash, with no input on stdin. Nothing is added to your script: start it withset -euif a failing line should stop it. - Working directory. Each script has its own directory on the runner,
under
$HOME/work/, and starts in it. Its path is inFLIGHTDECK_WORKSPACE. - Files persist. The directory is not cleaned between runs, so a checkout or a build cache from the last run is still there. Resetting the runner wipes it.
- Variables. Every variable and secret of the organisation and of the run's environment is exported, then the run's parameters. Which value wins covers clashes.
- Tools. Whatever the runner's setup installed, plus the
flyghtcommand. SDKMAN and nvm are loaded when the runner has them. - Exit code. 0 is success. Anything else marks the run failed and is recorded on it.
Shared boxes
Runs on the same runner share one machine. Two scripts running at once can see each other's files and processes, so keep a runner for work that trusts each other.
The flyght command
flyght is a small command placed on the path of every run. It has one job so
far: checking out a GitHub repository.
flyght git checkout <url> <branch> [--into <dir>] [--keep] [--provider github]- The first run clones the branch, shallow, into a directory named after the repository
(or the one
--intonames). - Later runs find the checkout already there, fetch, and move to the branch's tip. Tracked
files are restored and untracked ones removed, so the tree is as clean as a fresh clone.
--keepleaves untracked files, such as a build cache, in place. - For a private repository, keep a token in a secret named
GITHUB_TOKEN. The command reads it from the environment and never writes it into the checkout. - Only
github.comis accepted. A self-hosted GitHub needs--provider github.
Its progress lines start with flyght:. A failure prints
flyght: error with a short code and the reason, and exits 1, for example
needs a token and GITHUB_TOKEN is not set or
Branch release not found. flyght help prints the usage and
flyght version the version.
Environments
An environment is a named set of variables and secrets in a workspace, such as dev, staging and production. A run uses one environment, picked in the Console. Environments are optional: a workspace with none still runs scripts, with the organisation's variables only.
- Create. Press + on the Environments page. The new environment opens with its name ready to type over.
- Default. One environment is the workspace's default, marked with a Default tag. Runs use it unless the Console picks another. Change it with Set as default.
- Duplicate copies an environment with all its variables and secrets.
- Delete removes it and its variables.
The sidebar also has an Organisation row. It holds the variables shared with every workspace in the organisation, described below.
Variables and secrets
An environment has two tables: plain variables and secrets. Press + Add variable or + Add secret, type a name, press Tab, type the value. Edits save by themselves a moment after you stop typing.
- Names follow the same rule as parameters: letters, digits and
_, not starting with a digit. - A name is used once per environment, across both tables.
- The search box filters both tables by name.
Secrets
A secret's value is encrypted and write-only. Once saved it shows as
•••••••• and is never shown again, to you or anyone else.
- To change a secret's value, delete it and add it again. Its name can be edited in place.
- Deleting a saved secret asks you to type its name, because the value cannot be recovered and runs that use it will fail.
- A secret stays a secret. It cannot be turned into a plain variable.
Masking is a seatbelt
Run logs replace any secret value of four or more characters with
****, so an accidental echo "$TOKEN" does not leak it. A script
that transforms the value first, for example with base64, defeats the mask.
Anyone who can edit and run a script can read the secrets its runs are given.
Organisation variables
Variables and secrets set on the organisation reach every run in every workspace of it,
whichever environment the run uses. Manage them on the
Organisation row of the Environments page, or under
Settings, the organisation, Variables. They suit values that are
the same everywhere, such as a GITHUB_TOKEN.
Which value wins
A run's environment variables are built in three layers. When the same name appears in more than one, the later layer wins.
| Order | Source | Notes |
|---|---|---|
| 1 | Organisation variables and secrets | Always present. |
| 2 | The run's environment | Overrides the organisation's. |
| 3 | The run's parameters | Override both. {{NAME}} references are resolved against layers 1 and 2. |
Everything is exported: there is nothing to switch on per variable. A script reads
$AWS_REGION directly once a variable of that name exists.
Runners
A runner is the machine your scripts run on: a persistent Linux box, built once from a setup script and kept between runs. A fresh box is Ubuntu with Java 25, Node 24, Python 3.13, git and curl. The setup script adds whatever else your scripts expect.
- One box, many runs. Every run on a runner uses the same machine, and what a run leaves on disk is there for the next.
- Setup and memory are fixed. They are part of the box. A different setup is a new runner, which the wizard can start from a copy of an existing one.
- Workspace or organisation. A workspace runner is used from its workspace. An organisation runner can be used from every workspace in the organisation; it is created from the Organisation group of the Runners page or under Settings, the organisation, Runners.
- Default. One runner is the workspace's default. Runs use it unless the Console picks another.
Creating a runner
Press + on the Runners page. The wizard has five steps.
-
Start. Choose Copy (the same setup as one of your runners, on a new box), Templates (a preconfigured setup) or Custom (write the setup yourself).
-
Choose. Pick the runner to copy or the template. Either can be edited on the next step.
-
Setup. Edit the setup script and choose the memory the box may grow to: 2, 4, 8 or 16 GB. The Suggestions panel adds ready-made blocks; order matters, so install a tool before a line that uses it. The script stops at its first failing line.
-
Preview (optional). Runs the setup on a fresh box and shows its log, so you can see it work before keeping it. A previewed box becomes the runner. If you skip this, the setup runs when you create the runner.
-
Finish. Name the runner and, optionally, list the domains it may reach. Press Create runner.
A setup has 30 minutes to finish. If it fails the box is thrown away and the wizard shows the log: go back, fix the script and preview again.
Templates and suggestions
| Template | What it installs |
|---|---|
| Java 25 + Maven + Docker | Maven 3.9 and a Docker daemon beside the box's Java 25, for Maven builds and Testcontainers suites. |
| Java 17 + Maven | Temurin 17 through SDKMAN alongside Java 25, with Maven 3.9. |
| Python 3 + uv | uv beside the box's Python 3.13, for fast, locked dependencies. |
| Node + pnpm | The box's Node 24, raised, with pnpm through corepack. |
| Docker | A Docker daemon on the box. |
| Flyghtdeck | Java, Maven, Docker, Node, Playwright's Chromium, the AWS CLI and the Fly CLI: what Flyghtdeck's own scripts use. |
The Suggestions panel on the Setup step offers the same pieces one at a time: the AWS CLI, Docker, the Fly CLI, a git identity, Git LFS, Gradle, Java 17, 21 and 25, Maven, Node, pnpm, Playwright browsers, Python, uv, and a block for any Ubuntu package.
Runner settings
A runner's Settings tab holds what can change after it is built.
| Setting | What it does |
|---|---|
| Name | Shown in the Console's runner picker. |
| Run timeout | Seconds before a run on this box is killed. 3600 (an hour) by default, 24 hours at most. |
| Network | The domains the box may reach, one per line. See below. |
| Default runner | Makes this the workspace's default. |
| Reset the box | Wipes everything runs have left on it, back to exactly what the setup produced. |
| Delete this runner | Destroys the box and everything on it. |
Network
Leave the list empty and the box reaches anything. Add domains and it reaches only those:
github.com
*.npmjs.org
s3.eu-west-2.amazonaws.com
A * covers one level of subdomain. The limit is applied from outside the box,
so a script cannot widen its own reach, and it applies after setup, so a build can still
install what it needs.
Reset and delete
- Reset removes checkouts, caches and anything installed by hand. The box restarts and is back in a moment. It is refused while runs are queued or running on the runner, and the runner shows Resetting… until it is back.
- Delete is permanent. Scripts that picked the runner fall back to the workspace's default. Stop any runs on it first: a run in flight on a deleted runner fails.
Workspaces
A workspace holds the scripts, environments and runners for one job of work, and belongs to an organisation. The switcher in the top bar lists the workspaces you can see and keeps you on the same page when you change.
- Create with + New workspace in the switcher.
- Rename or Delete from the ⋯ beside a workspace in the switcher.
- Deleting removes its scripts, environments, runners and run history. You are asked to type the workspace's name, and it is refused while the workspace has runs in flight.
Organisations
Every account has a personal organisation, made at sign-up. It is yours alone: it cannot be renamed, deleted or shared. To work with other people, create an organisation: Settings, then the + beside Organisations. Creating one makes you its owner and switches you into it.
When you belong to more than one, the account menu lists them. Pick one to act in it; the workspace switcher then shows that organisation's workspaces.
An organisation's page in Settings has up to five tabs.
| Tab | What it does | Who sees it |
|---|---|---|
| Members | The people in the organisation and pending invitations. | Everyone |
| Groups | Bundles of permissions and the members who hold them. | Owner |
| Variables | Variables and secrets shared with every workspace. | Owner, and members granted it |
| Runners | Runners every workspace can use, and the default for new workspaces. | Owner, and members granted it |
| Settings | Name, picture and deleting the organisation. | Owner |
Deleting an organisation removes its workspaces with their scripts, run history and secrets, and every member's seat. It asks for the organisation's name, then for a second confirmation when other members are affected.
Members and invitations
-
The person joining signs in and copies their Account reference from Settings, Profile, then sends it to you.
-
On the organisation's Members tab press + Invite member and paste the reference.
-
They receive an email with a link, if their account has an email address. You can also copy the link from the invitation's ⋯ and send it yourself. Only the invited account can open it.
-
They choose Join organisation. Put them in a group, because a member in no group sees nothing.
- Revoke withdraws an invitation that has not been accepted.
- Remove takes a member's seat and group memberships. Everything they made stays.
- Leave is on the Members tab for members. The owner cannot leave.
Groups and permissions
The owner can do everything and is the only one who manages members, groups and the organisation itself. Everyone else gets access through groups: a group is a set of members and the permissions they hold. Access is only ever handed out. A member in no group sees no workspaces, and a new workspace is hidden from every group until it is granted.
Simple: pick a level
| Level | What it allows |
|---|---|
| Admin | Everything inside the organisation. |
| Editor | Creates, edits and runs things; no deleting. |
| Runner | Sees everything and runs scripts. |
| Viewer | Sees everything, changes nothing. New groups start here. |
A level applies to the organisation and to every workspace in it.
Advanced: tick by tick
The Advanced view is a matrix of Read, Run, Create, Edit and Delete. A group whose ticks match no level reads Custom.
- Organisation: creating workspaces, and the organisation's own variables and runners.
- Workspace access, one section per workspace: the workspace itself, its scripts (including Run), its environments and its runners.
- Read on a workspace makes it visible. Without it the rest of that section does not apply. Seeing a workspace means seeing its scripts.
Permission changes are a draft until you press Save; Revert restores what was saved. Adding or removing a group's members takes effect immediately.
Signing in
Sign in with your GitHub account. Flyghtdeck is in early access, so creating an account also needs the early access key from your invitation. Enter the key, then continue with GitHub.
The first sign-in opens a setup sheet: pick a theme and your notifications, then optionally a first workspace, runner and script. Nothing is created until you press Finish, and you can leave the sheet at any point.
Notifications
When a run you started finishes, a notification lands in the bell at the top of the app. Click one to open that run in History.
| Setting | What it does | Starts |
|---|---|---|
| Successful runs | A run finishes cleanly. | Off |
| Failed runs | A run fails or times out. | On |
| Email notifications | Also sends them to your account's email address. | Off |
| Desktop notifications | Also shows them as system banners while a Flyghtdeck tab is open. This browser only. | Off |
- Only the person who started a run is notified.
- A run that was cancelled or stopped does not notify.
- The bell keeps your latest 100 notifications. Clear all empties it.
Settings
Settings are under the account menu at the top right.
| Section | What is there |
|---|---|
| Profile | Your name, sign-in method, email and account reference; sync from GitHub; sign out; delete account. |
| Appearance | Dark or light theme, kept in this browser. The toggle in the top bar does the same. |
| Notifications | Which run outcomes reach you, and how. |
| Organisations | One page per organisation, and creating a new one. |
| Feedback | A message straight to the team. |
| Advanced | Usage statistics: reports which pages and features you use, tied to your account reference; never names, script contents or logs. On by default. |
Deleting your account
Profile, Advanced, Delete account. You are asked to type your username. It is permanent: your account goes, with the workspaces you created, their run history, and any organisation you were the only member of. Your GitHub account is not touched.
Plan limits
Limits belong to an organisation and follow its owner's plan. On the starter plan they are currently:
| Limit | Starter | At the limit |
|---|---|---|
| Runs at once | 1 | More runs wait in the queue. |
| Workspaces | 3 | New workspace shows Limit reached. |
| Members | 3 | Invite member shows Limit reached. The owner and pending invitations count. |
| Runners per workspace | 10 | The wizard refuses with the reason. |
| Organisation runners | 10 | The wizard refuses with the reason. |
| Runners building at once | 3 | Wait for a build to finish. |
There is no limit on scripts, environments or variables.
Example scripts
Check out a private repository and build it
Runner: the Java 25 + Maven + Docker template. Secret:
GITHUB_TOKEN, a token that can read the repository. Parameter:
BRANCH, text, default main.
set -eu
flyght git checkout https://github.com/acme/api.git "$BRANCH"
cd api
mvn --batch-mode packageThe checkout stays on the runner, so the next run only fetches what changed and Maven's cache is already warm.
Deploy a static site to S3
Runner: add the AWS CLI 2 suggestion to the setup. Variables:
BUCKET, DISTRIBUTION and AWS_DEFAULT_REGION. Secrets:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and
GITHUB_TOKEN. The AWS CLI reads its variables by itself. Turn on
Require confirmation to run.
set -eu
flyght git checkout https://github.com/acme/website.git main
aws s3 sync website/public "s3://$BUCKET" --delete --no-progress
aws cloudfront create-invalidation --distribution-id "$DISTRIBUTION" --paths '/*'
Keep one environment per target, each with its own BUCKET,
DISTRIBUTION and keys. The same script then deploys wherever the Console's
environment picker points, and the keys can never belong to a different site than the
bucket.
Back up a Postgres database
Runner: add the apt packages suggestion with
postgresql-client, and AWS CLI 2. Variables:
PGHOST, PGDATABASE, PGUSER and
BACKUP_BUCKET. Secrets: PGPASSWORD and the AWS key pair.
pg_dump reads the PG* variables by itself.
set -eu
stamp="$(date +%Y%m%d-%H%M%S)"
dump="/tmp/$PGDATABASE-$stamp.dump"
pg_dump --format=custom --file="$dump"
aws s3 cp "$dump" "s3://$BACKUP_BUCKET/" --no-progress
rm "$dump"
echo "Backed up to s3://$BACKUP_BUCKET/$PGDATABASE-$stamp.dump"Name variables after the tool
Give a credential the name its tool already reads, such as
GITHUB_TOKEN, AWS_ACCESS_KEY_ID or PGPASSWORD. The
script then needs no plumbing, and the value never appears on a command line.
Messages you may see
| Message | What to do |
|---|---|
| No runners. Create one on the Runners page | The workspace has nothing to run on. Create a runner. |
| Runner is resetting | Wait for the reset to finish, then run again. |
| Missing required parameters | Fill in the fields marked * in the Console. |
| This script changed elsewhere | Press the refresh icon in the Scripts sidebar. |
| Script already has a run in flight | Wait for the run to finish, or stop it. |
| Script exceeded the time limit | Raise the runner's run timeout, or make the script faster. |
| Run waited longer than the limit | The queue did not clear within an hour. Check for a stuck run in the Queue and start it again. |
| Lost touch with the server | The run was stopped because its runner lost contact with Flyghtdeck. Run it again. |
| Limit reached | The organisation is at a plan limit. |
Getting help
Flyghtdeck is in alpha and changes often. If something is rough, missing or wrong in these pages, send it from Settings, Feedback, or write to admin@flyghtdeck.com.