Workstation

A development machine Ringleader creates and manages for you: a VM on your laptop or in your cloud account.

A Workstation is a development machine that Ringleader creates and manages for you: a VM on your laptop (vz on macOS, WSL2 or Hyper-V on Windows, or qemu on Linux) or in your cloud account. You describe the machine you want (OS, tools, code, settings) and Ringleader builds it and keeps it matching that description.

So you can delete the machine and get an identical one back, hand a teammate or a coding agent the same environment from the same file, and change the environment by editing the description instead of shelling in.

This page is the field-by-field reference. spec is the description you write; status is what the control plane and the in-machine agent report as actually true.

apiVersion: workstations.ringleader.dev/v1
kind: Workstation

A workstation is namespaced. Its effective configuration is the merged configuration of every WorkstationConfig that attaches to it (by selector or explicit reference) plus the workstation’s own inline spec. That merge is computed on demand rather than stored in status. Read it with rl workstation get-resolved-configuration.

Minimal example

Everything is implicit: a provider is chosen by automatic placement, and configs attach by selector.

apiVersion: workstations.ringleader.dev/v1
kind: Workstation
metadata:
  name: hello-ws
  namespace: dev
  labels:
    tier: dev
spec: {}

Spec fields

You author these. Many are config-layer fields: they can also be set on a WorkstationConfig and merged in.

Placement & provider

FieldTypeDescription
requirements[]stringMachine requirements in precedence order (e.g. ["provider:gcp"]); matched against discovered provider capabilities.
providerstringExplicit provider pin, shorthand for requirements: [provider:X].
providerConfigobjectPer-provider opaque config (e.g. memory/cpus for local VMs, or {gcp: {project, zone, machineType}}). Not merged from WorkstationConfig config layers, though the matching CloudIdentity’s defaultProviderConfig and overrideProviderConfig are folded around it.
machineTypestringProvider machine size class (e.g. e2-micro for GCP).
securityProfilestringNamed security profile (e.g. baseline, privileged).

Lifecycle

FieldTypeDescription
stoppedbooltrue stops the workstation. The machine is halted and rests in the Stopped phase. A workstation created with stopped: true boots once, applies its first configuration, and then powers off.
restartNoncestringChange this on a running workstation to trigger a Stop→Start restart.
lifecycleobjectWhen the workstation stops or is deleted on its own: {idle, maxRunTime, ttl, ttlAction}. See How long a workstation lives.
ttlstringThe older top-level spelling of lifecycle.ttl, still accepted. Declaring a lifetime here and in lifecycle is rejected.
ttlActionstringThe older top-level spelling of lifecycle.ttlAction, still accepted: delete (the default) or stop. Only consulted when ttl is set.
exchangeSecretNoncestringChange this to rotate the workstation’s own credential, which is what to do if one leaks. Like restartNonce, only the change matters, never the value; absent means nothing to rotate.

How long a workstation lives

A workstation nobody turned off keeps costing money, and a stopped cloud VM still bills for its disks. spec.lifecycle declares deadlines the control plane enforces on its own, with no client involved. It exists for the case where nothing is left to clean up after you: a CI job killed outright, or a machine you meant to keep for an afternoon.

There are three things you can ask for, and they combine. Stop the workstation when nobody is using it, with idle. Stop it once the current run has gone on long enough, with maxRunTime. Delete it at a deadline, with ttl.

apiVersion: workstations.ringleader.dev/v1
kind: Workstation
metadata:
  name: ci-run-4821
  namespace: dev
spec:
  lifecycle:
    idle:
      timeout: 30m         # stopped after 30 minutes with nothing going on
      action: stop
      sshSessions: true    # an SSH session means somebody is using it
    maxRunTime: 8h         # stopped once this run has lasted 8 hours
    ttl: 7d                # deleted a week after it was created

If that is all you need, you are done. The rest of this section says what each field does on its own.

Every duration is a plain unit string (45s, 90m, 2h, 720h) or a whole number of days (7d). It must be positive. A value that cannot be read, or a zero or negative one, is rejected when you apply it rather than being silently ignored.

Stopping an idle workstation. lifecycle.idle powers the machine down after a period in which nothing you named as activity happened. A probe inside the workstation reports what it sees, and the control plane decides.

spec:
  lifecycle:
    idle:
      timeout: 45m
      action: stop
      sshSessions: true
      processes: ["pytest"]
      ports: [5432]

The block takes a timeout, an action, and the conditions that say what “in use” means for this workstation.

  • timeout is how long every condition must agree the workstation is idle before it stops.
  • action accepts only stop. A workstation that should go away declares ttl with ttlAction: delete instead. A machine that reported itself idle and then went quiet keeps its last verdict and its clock, so the timeout still elapses, which is safe only because a stop can be undone.
  • sshSessions: true counts a person’s SSH session as activity.
  • processes lists executable names as they appear in /proc/<pid>/comm, at most 15 characters each. A path or a wildcard is rejected when you apply it, because the probe compares the executable name and never the command line.
  • ports lists ports on the machine, and counts established connections rather than listening sockets. A dev server nobody has connected to is exactly the workstation this is meant to power down.
  • checkCommand is the last resort, for activity none of the others describes. A non-zero exit means the machine is in use. It runs as root on a repeating timer, so it may use only letters, digits, spaces and - _ . / = : , @ + %, at most 256 characters. Put anything longer in spec.files and name the script.

Every condition you declare must agree the workstation is idle before the timeout runs, and there is no setting that weakens that. Declaring more conditions makes a stop less likely, never more. A timeout with no condition is rejected, and so is a condition with no timeout.

Stopping a workstation after a fixed run. lifecycle.maxRunTime powers the machine down once the current run has lasted that long, whatever is happening on it. The anchor is the run rather than the creation time, so starting the workstation again gives it a fresh period: one that stops in the evening can be started the next morning. The status message names the field that stopped it and tells you to start the workstation to begin another run.

It is rejected together with ttlAction: stop, which is the older spelling of the same thing.

Deleting a workstation at a deadline. lifecycle.ttl is the delete deadline, measured from when the workstation was created. Its action is ttlAction.

  • ttlAction: delete is the default. It removes the workstation and destroys the machine and its disk, exactly as rl workstation delete would. The clock starts at creation and nothing moves it, not a stop, a start, or a restart. Editing the value is the one thing that moves the deadline, and it takes effect immediately.
  • ttlAction: stop is the older spelling of maxRunTime. It powers the machine down and leaves it there, so its disk and state survive and you can start it again. Its deadline is anchored on the current run rather than on creation.

Two things hold whichever deadline you declare. Declaring a lifetime twice is rejected when you apply it: the lifecycle block is read whole, so spec.ttl beside spec.lifecycle.ttlAction is rejected as well, not only the same field written in both places. And enforcement is periodic, so a workstation can sit briefly past its deadline before the action lands. Treat a deadline as “at or shortly after this”, not as a precise instant.

The next deadline is published as status.expiresAt, and rl workstation get grows an EXPIRES column, counting down or reading expired, as soon as any listed workstation declares one.

A lifetime is per workstation, never per config

lifecycle, ttl and ttlAction are Workstation fields only. They are rejected on a WorkstationConfig: a config attaches by selector, so a lifetime declared there would expire workstations its author does not own. Set it on each workstation that should expire.

Configuration attachment

FieldTypeDescription
configs[]objectExplicit WorkstationConfig references: {name, priority}. The priority is taken from the reference verbatim, with no default, so an omitted one is 0, the lowest layer of all. See merge order.

Machine contents (config-layer fields)

FieldTypeDescription
imagestring/objectOS image: a shorthand string, or {os, distribution, version}.
packages[]Packages to install: each a bare name or {name, version, updatePolicy}.
devtools[]objectCurated install recipes: {name, version} (e.g. nodejs, claude-code, vscode-web).
toolconfigs[]objectPer-tool config channel: {id, name, config} (opaque config) for already-installed tools.
packageRepositories[]objectThird-party repos: {name, type, urls, key, …} (apt/ppa).
aptSources[]Legacy apt repos + keys (normalized to packageRepositories).
scripts[]objectProvisioning scripts: {name, phase, content, …}.
sources[]objectCode/repos synced into the workstation: {name, git|local, path, updatePolicy, …}. A source can also run its repository’s own devcontainer.
files[]objectDeclarative files materialized on the machine.
services[]objectUser-declared managed services (systemd units / command-mode).
ports[]intDeclared listening ports.
dotfilesRepoobjectA dotfiles repository cloned into the machine and installed by running its own install script.
trustedFolders[]stringAbsolute paths the machine’s coding tools pre-trust, on top of every sources[].path.
sshKnownHosts[]Outbound SSH host-key trust pinned into ~/.ssh/known_hosts: a bare hostname, or {host, keys}.
tailscaleobjectJoin the machine to a Tailscale tailnet. The join credential is always a Secret reference.
egressobjectRestrict what the workstation may connect out to: {enforcement, destinations[]}. Absent means no restriction: everything your network routes. See Restricting outbound connections.

System settings (config-layer fields)

FieldTypeDescription
identityobjectThe login user: {user, uid, gid, group, groups, shell, sudo}. The only place the login user is declared; the agent owns user creation. Default shell /bin/bash; the user gets passwordless sudo by default (sudo: false to opt out).
environmentmapSystem-wide env vars → /etc/environment + profile.d.
dotfilesmapShell-dotfile fragments: path → content (managed blocks).
sysctlsmapKernel sysctls → /etc/sysctl.d/.
limits[]pam_limits entries → /etc/security/limits.d/.
hostnamestringMachine hostname.
timezonestringIANA timezone (e.g. America/New_York).
localestringLocale (e.g. en_US.UTF-8).
securityUpdatesboolEnable unattended security upgrades (Debian: unattended-upgrades).
diagnosticsobjectLedger config: {ledger: {enabled, retain, maxOutputBytes}}. maxOutputBytes limits captured stdout and stderr for each step. A value above 128 KiB resolves to 128 KiB.
defaultLocalBindingobjectDevice-local forwarding template; the daemon seeds a LocalBinding from it once when the workstation first reaches Running (honor-delete).
capabilities[]stringWhat the workstation’s own identity may do: self:read, self:power, sa:assume. Both the admin and member tiers must declare a capability for the machine to hold it. See Workstation capabilities.

Status fields

The system writes these. They are read-only.

FieldTypeDescription
phasestringPending, Provisioning, PostInstall, Running, Reconfiguring, Starting, Stopping, Stopped, Failed, or Terminating.
messagestringThe one human headline: what is going on with this workstation, in a sentence.
providerstringSelected provider (e.g. gcp, vz, qemu, lima).
cloudIdentitystringOn a cloud workstation, the CloudIdentity whose selector its labels match. <none matched> when none does, and the workstation then uses the deployment’s own cloud credential. Empty on a local workstation.
providerIDstringProvider-specific machine ID (e.g. the GCE instance name).
osstringThe operating system the workstation’s merged configuration selects, as a short name such as debian or ubuntu.
osVersionstringThe version from the same selection, such as 13. Empty when the selection names none.
imagestringThe base image the machine was actually created from.
machineTypestringThe size the provider reports the machine running as. It changes on the first status read after a resize.
rootDiskobject{observedBytes}: the size of the root filesystem the machine measured. See RootDiskShortfall.
addressstringSSH address reachable by the daemon (absent for a NAT’d VM the daemon can’t dial).
internalAddressstringThe machine’s address inside your cloud network. Empty on a local workstation.
networkTags[]stringThe cloud network tags the machine carries, including ones Ringleader adds. On GCP they decide which firewall rules apply to it.
managementAddressstringThe host:port on an edge instance that reaches this workstation’s SSH while the edge instance routes its traffic. Ringleader connects there instead of address. Empty on every other workstation.
userstringResolved SSH login user.
observedGenerationintThe spec generation the controller last observed.
expiresAtstringWhen the next lifetime deadline falls due, as a timestamp: the delete deadline, or the run limit while the workstation is running, whichever comes first. Absent on a workstation that declares neither.
configobjectConfiguration state: {effectiveHash, appliedHash, appliedAt}. effectiveHash is the configuration the control plane wants; appliedHash is what the machine last reported applying.
egress.inputsstringOn an hcs, vz or qemu workstation served by its own network process, the fingerprint of the store facts its Edge confirmed. It never contains a credential value or digest.
agent.seenAtstringWhen the control plane last heard the in-VM agent keep its connection open, as a timestamp rounded down to a five-minute boundary. rl workstation describe prints it as Last heard. Present only on a workstation whose agent connects out to Ringleader for its configuration, and only once the agent has connected. See AgentNotReporting.
conditions[]objectConditions, each with a reason and a message. See Conditions and their reasons.
configStateobjectPresent while a configuration pass runs, and when the pass keeps failing. See Fields that report a problem.
nonFatalConfigobjectPresent only while an optional configuration step is failing. See Fields that report a problem.
ambientAgentobjectPresent only while ambient SSH_AUTH_SOCK delivery has come up short. See Fields that report a problem.
diagnosticsobjectLedger rollup (path + last-run counts).
sshHostKeys[]stringThe workstation’s SSH host public keys, for pinning into known_hosts.

Fields that report a problem

Three status fields report trouble. A healthy workstation carries none of them, apart from configState while a configuration pass runs. Their absence is normal, and does not by itself prove that anything succeeded.

configState reads {state: configuring} while a configuration pass runs, and {state: failed} when the in-VM agent reports that its configuration pass keeps failing. The failed state carries the same fact as the Configured condition’s ConfigurationFailed reason, which rl workstation describe prints. It also names the failing step ({kind, target}) and a cause: disk-full, permission-denied, read-only-filesystem, network-unreachable, step-timeout or other. The Configured message and rl troubleshoot both name the cause and what to do about it.

nonFatalConfig appears while an optional step is failing: a step from a personal layer, a dotfiles fragment, a dotfiles repository, or a personal Integration’s endpoint file. An optional step blocks nothing, so the workstation still reports Configured. rl workstation describe prints it as Optional configuration, and rl troubleshoot has the failing step and its output. It clears on recovery, and on a power cycle along with the rest of the configuration verdict.

ambientAgent appears while ambient SSH_AUTH_SOCK delivery has come up short, as {profile, sshd, reason, detail, observedAt}: the two booleans say which of the env files are in place, and reason names the precondition that stopped the other. A workstation your daemon has never connected to also carries no ambientAgent, so absence alone does not prove delivery worked. rl workstation describe prints it as Ambient SSH agent, and rl troubleshoot confirms the live state with its ssh-agent-env probe. See SSHKey.

Status does not carry the merged configuration

There is no status.resolved. Status carries a fingerprint of the effective configuration (status.config.effectiveHash), not the configuration itself: the merge is derived data, re-computed on demand. To read what a workstation actually resolves to, use rl workstation get-resolved-configuration, which prints the whole merged spec including the environment, files, scripts, sources, devtools, toolconfigs, and dotfiles.

Lifecycle phases

Stopping and Starting are real phases, not instant transitions: a power operation is issued, and it rests in them until the machine is confirmed down (or back up). Starting means an existing machine is powering on, as opposed to Provisioning, which means one is being created.

A workstation is fully configured when status.config.appliedHash equals status.config.effectiveHash and its Configured condition is True.

A configuration verdict never outlives the boot that produced it: stopping a workstation clears appliedHash and sets Configured to False, so a stopped workstation never claims a configuration it applied on a previous boot.

Conditions and their reasons

Available, Ready and Configured are always published. The others appear only when there is something to report. Each condition carries a reason (a short machine token) and a human message. Available is the one to gate on: it is the aggregate that answers “is this workstation usable yet?”, and the CLI, the TUI and the desktop apps all fold it into the single status they show. Available takes Configured and ToolsReady into account. The conditions listed after ToolsReady below are not taken into account, so a workstation can be Available while one of them reports a problem.

ConditionMeaning
AvailableThe aggregate readiness signal: True only when the workstation is Running and fully configured.
ReadyThe machine itself is up and reachable. Says nothing about configuration.
ConfiguredThe in-VM agent has applied the configuration the control plane wants.
ToolsReadyDevtool installation, and usually absent: it is published only once a tool has failed to install. A workstation whose tools all installed cleanly carries no ToolsReady condition at all.
EgressEnforcedWhether the workstation’s declared egress policy is actually being enforced. Published while egress is declared, and while an attached credential injection Integration asks an edge instance to inject or inspect for the workstation. Removed when neither holds.
EgressAppliedOn an hcs, vz or qemu workstation served by its own network process, whether that process holds the rule set and credentials that its current Integration, Secret and policy inputs resolve to. True has reason Applied; False has reason InputsOutstanding and names what remains. Run rl workstation wait <name> --for=EgressApplied after one of those inputs changes.
EgressCredentialOn lima only: whether an attached credential injection Integration’s key is being added, for a workstation with no egress policy. See Credential injection.
SteeredWithoutServingOn AWS and Azure: an edge instance routes this workstation’s subnet and holds no rule for it. Present only while that is true.
RootDiskShortfallThe root filesystem is much smaller than the disk size declared.
AgentNotReportingThe control plane has not heard the in-VM agent for more than 15 minutes.
SSHAdmissionMissingRingleader could not put its own firewall rule admitting SSH in place, on a cloud workstation.
PublicAddressChangeNotAppliedThe public address setting changed after the machine was created, and the machine keeps its original setting.

Available reasons

ReasonMeaning
ReadyRunning and fully configured, so the workstation is usable.
ConfiguringRunning, but the configuration is still being applied.
ToolsInstallingRunning and configured, but declared tools are still installing.
ProviderUnavailableThe provider this workstation was created on is not available on this machine. A local workstation that names no provider stays on the one it was created with, even when your default changes. It carries on once that provider is available again.
(a phase name)Not running. The reason is the phase itself (Provisioning, Stopped, Failed, …).

Configured reasons

ReasonMeaning
AppliedThe in-VM agent reported an applied configuration matching the one requested.
ConfiguringWaiting for the in-VM agent to apply the current configuration.
ConfigurationStalledThe configuration has been waiting past the grace period and the agent has reported nothing at all.
ConfigurationFailedThe agent reported that its own configuration pass keeps failing.
ConfigurationNotCompletingThe agent reported that a pass is running, and it has been outstanding past the grace period without finishing.
ConfigurationNotDeliveredThe control plane’s configuration update has not reached the workstation for some time. The workstation is healthy and still applying its previous configuration.
StoppedThe workstation is powered down, so nothing is being applied.
UserConfigFailedThe login user, groups or sudo could not be configured.

The first three differ in what is actually known, and that is what decides where to look:

  • ConfigurationStalled is silence, so it stays a diagnosis rather than a verdict. A failed boot-time agent bootstrap is the usual cause, but a slow install looks identical from outside, and the workstation is neither failed nor destroyed.
  • ConfigurationFailed is a report, so it names a real problem. Read the agent log and the diagnostic ledger (status.diagnostics), or run rl troubleshoot.
  • ConfigurationNotCompleting means the agent is alive and a pass is outstanding, so the boot console and the agent service are the two places not to look. A slow step such as a large image build and a pass that will never return look the same from outside; the workstation’s own configuration log and on-box ledger name the step.

ConfigurationNotDelivered is the only reason here that does not send you inside the machine, because the problem is upstream of it. UserConfigFailed is about the login user rather than the applied configuration, so a workstation can carry it while its configuration hashes match exactly.

While ToolsReady is present its reason names the step that failed; it flips to Installed once a workstation that previously failed has every declared tool present.

EgressEnforced reasons

The reasons on the False arm say the declared policy is not in force, and why:

ReasonMeaning
ProviderCannotEnforceThis workstation’s provider enforces nothing outside the machine: lima and wsl2. The restriction is recorded on the workstation and not in force. To enforce it, name a cloud provider, name hcs on Windows, name vz on a Mac, or name qemu on Linux.
NoEdgeThe policy names a host, or an attached Integration injects a credential or inspects a host, and no Edge serves this workstation. On a cloud there is none for its provider and region. On hcs, vz and qemu it means neither the workstation’s own network process nor an edge VM can serve it, because it was created before its provider gave each workstation its own address, or, on qemu, before Ringleader could set up the network cards its own Edge needs. With a bestEffort policy, nothing in the policy is enforced, including your cidr destinations, because a rule set is built whole or not at all. A workstation that was already enforcing an earlier policy keeps that one instead, and the message says which. If that earlier policy was enforced behind an edge instance, keeping it restricts nothing. With a strict policy the workstation is held closed. To fix it on a cloud, a namespace administrator declares an Edge, or you declare every destination as a cidr. On hcs, vz and qemu, delete the workstation and create it again.
GatewayNotServingAn Edge serves this workstation and is not holding its policy yet. On hcs, vz or qemu with no Edge declared, that Edge is the workstation’s own network process. Otherwise the Edge’s VM may still be being built. In either case the Edge may not have confirmed the policy, or it may not be able to serve part of what you declared. On AWS and Azure, a common cause is a workstation in a subnet that already has a route table of its own: move it to the subnet your onboarding reserved for the workstations an Edge serves. On a cloud, a workstation with an egress policy is held closed meanwhile, whatever its enforcement. On hcs, vz and qemu the message says which state the workstation is in: still on the open network, behind its Edge and reaching nothing yet, or, behind an edge VM, one that is not forwarding. Ringleader restarts an edge VM that is not forwarding. For a declared Edge, run rl edge describe <name> to see its own status.
SteeringHeldOn a cloud, the edge instance is deliberately not routing this workstation yet: its management port is not in place, or, on AWS and Azure, another machine in its subnet is holding the subnet back. The workstation stays reachable at its own address, and is held closed meanwhile. The Edge’s message names what it is waiting for.
SubnetSteeredElsewhereOn AWS and Azure, another edge instance already routes this workstation’s subnet, and a subnet can be routed by only one. Nothing in the workstation’s manifest fixes this, so ask a namespace administrator. The workstation is held closed meanwhile.
BoundaryIncompleteEvery declared destination is held, and the workstation can still reach undeclared addresses inside its own network without going through the edge instance, because the edge instance could not read the network’s address ranges. It is reported instead of any reason on the True arm. Check the permissions of the cloud identity that builds the Edge, change the network’s addressing, or remove the Edge.
EndpointUnreachableEverything you wrote fits, and an endpoint an attached Integration points at could not be given to the firewall: it did not resolve, or it resolved to nothing an address rule can hold. So that it can still reach its Integration, the workstation runs unrestricted. A strict workstation behind an edge instance is held closed instead. What has to change is the endpoint, or the DNS resolver that could not answer for it.
DeploymentNotConfiguredYour policy is fine and the deployment is not set up to hold it. Nothing you can write fixes it, so ask whoever runs the control plane. A strict workstation behind an edge instance is held closed while this lasts.
RuleSetNotAttachedThe firewall objects exist and this workstation is not on them, so nothing is filtering its traffic. Re-applying usually rebuilds nothing, because nothing has been edited. What has to change is whatever is blocking the attach, which the message names.
RuleSetMissingThe firewall objects that were holding the policy are gone, most often deleted in your cloud account. The workstation is not restricted at all. Restart it (rl workstation restart <name>) to rebuild them: powering a workstation on re-creates its objects. Re-applying does nothing, because nothing you wrote has changed and an unchanged manifest never reaches the provider.
RuleSetIncompleteSome of the objects have been deleted while the rest stand, so the workstation is not restricted. Ringleader does not repair a partly deleted set by itself, because a delete in progress looks the same. Restart the workstation (rl workstation restart <name>) to rebuild it. Seen on gcp only, where a rule set is several objects sharing one name. On aws and azure it is a single object.
RuleSetDriftedThe objects are still there and no longer match the policy (a rule widened, a deny switched off) and Ringleader issued the repair and was refused. Rebuilding is not the fix, since the objects already exist under the right names: what has to change is whatever is denying the repair, which is usually a permission. Ringleader retries on its own cadence, so a corrected grant clears this without touching the workstation.

On a cloud, a workstation that is held closed reaches only what it needs to stay managed by Ringleader and the endpoints of its attached Integrations, until an edge instance serves its policy. It keeps running, and you can still edit it and reach it over SSH. On hcs, vz and qemu, a workstation that is held closed reaches nothing until an Edge serves it.

The reasons on the True arm say the policy is in force:

ReasonMeaning
EnforcedThe restriction is real. On a cloud workstation the message names the provider, how many destinations it holds, and the identifier of the firewall objects holding them, so you can check them in your own cloud console. On vz and qemu with a declared Edge, it names the edge VM on your machine that holds it. With no Edge, including on hcs, it names the workstation’s own network process.
CredentialOnlyThe workstation declares no egress policy, and an edge instance, an edge VM or, on hcs, vz and qemu, its own network process serves it because an attached Integration injects a credential or inspects a host. Its policy restricts nothing, and only the hosts the Integration names are decrypted.
InterceptionPendingOn hcs, vz and qemu: its Edge serves the workstation and restricts it as declared, and a host an attached Integration names is not being injected or checked. The message names the host and the cause: the rule’s allowDaemonPlacement gate is closed on this workstation (the rule sets false, or leaves it unset and the workstation’s owner is not the Integration’s author), the Secret is missing or empty, the author cannot read it, or the Edge is not reading that host yet.
InboundUnreachableThe workstation is enforced, and routing its traffic through the edge instance took away its own inbound path, so it no longer answers at its public address and rl shell, rl code and the live checks cannot reach it. You see this when the Edge sets inboundManagement: false or publicAddress: false, or on AWS and Azure when the edge instance began routing the subnet before this workstation’s management port was in place. Reach the workstation over your own private network.
DestinationUnreachableThe workstation is enforced, and a cidr destination cannot be reached while an edge instance routes it: on GCP and Azure any private address range, and on AWS a range inside the VPC. The message names each one. Declare a range outside those, or ask a namespace administrator whether this workstation needs an Edge.

When more than one of these applies, the reason is the first that applies of BoundaryIncomplete, InboundUnreachable, DestinationUnreachable, InterceptionPending and Enforced, and the message carries every sentence.

A strict policy is checked when you apply the workstation. The workstation must name its provider, and the namespace must hold an Edge for that provider wherever the policy needs one, which is for a host on a cloud. An hcs, vz or qemu workstation needs no Edge, because its own network process serves it. The check is on the merged policy. It does not require a matching region or a ready edge machine, and an Edge that is being deleted does not count. EgressEnforced reports what is actually held once the workstation is set up. See Where it can be enforced.

Conditions that report a problem

These conditions are absent on a healthy workstation, except EgressCredential. RootDiskShortfall, SSHAdmissionMissing, PublicAddressChangeNotApplied and AgentNotReporting turn True when something needs your attention, and False once it has cleared, so their last transition time records when it cleared. SteeredWithoutServing is present only while it applies. EgressCredential runs the other way: True is the healthy state.

ConditionReason while it appliesWhat to do
RootDiskShortfallRootDiskSmallerThanDeclared: the root filesystem measured under 80% of the declared size (rootVolumeGiB on AWS, diskGiB on GCP, osDiskGiB on Azure), or a disk was grown and the filesystem has not followed yet. The message gives both sizes.On AWS and GCP a larger disk takes effect at the next start, so restart the workstation. On Azure the message says what happened. It clears as RootDiskMatchesDeclared.
AgentNotReportingAgentSilent: the control plane has not heard the workstation’s in-VM agent for more than 15 minutes. The message gives the time it last did. The workstation itself may still be reachable.If you can reach the workstation, run rl logs <workstation> to read the agent’s log. It clears as AgentReporting once the agent is heard again, and the condition is removed when the workstation leaves Running.
SSHAdmissionMissingSSHAdmissionNotWritten: Ringleader could not create the firewall rule it keeps admitting SSH to this cloud workstation. The message has what the cloud refused. A rule your own onboarding created may still let you in.Usually a missing permission in your cloud onboarding. It clears as SSHAdmissionInPlace.
PublicAddressChangeNotAppliedPublicAddressSetAtCreate: the public address setting now resolves to a different value, because a CloudIdentity or the CloudAccount changed it. A public address is set when the machine is created.Recreate the workstation to apply it. An edit to the workstation’s own setting fails the workstation instead. It clears as PublicAddressMatchesWorkstation.
SteeredWithoutServingAn edge instance routes this workstation’s AWS or Azure subnet and holds no rule for it, so it reaches nothing, and nothing outside can reach it: SteeredFromAnotherNamespace, NotBoundToSteeringEdge, or NotServedBySteeringEdge.Put the workstation in another subnet, or, as the message says, declare a policy that edge instance serves. The condition is removed when it clears.
EgressCredentialOn lima, CredentialPending: a host an attached Integration names is reaching the API without the key. The message names the host and the cause.If the message says the allowDaemonPlacement gate is closed, set true on the rule to open it on every local workstation. Credential injection says what each value does. It reads True with CredentialHeld while the key is being added.

AgentNotReporting applies to a workstation whose in-VM agent connects out to Ringleader to fetch its configuration, which is the default for cloud workstations. A workstation that Ringleader configures over SSH never carries it. Ringleader counts the 15 minutes from the later of the last time it heard the agent and the moment the workstation reached Running, and that last time is the agent.seenAt status field. The condition counts toward neither Configured nor Available, so a workstation whose agent has stopped can still be Available.

See also

  • WorkstationConfig: the reusable layers a workstation merges.
  • Integration: attaching a shared service, and the file that lets a program on the workstation find it.
  • Edge: the VM that enforces a hostname destination and adds injected credentials.
  • Configuration tutorial: a worked browser-IDE example.