Skip to content

Workflows

Run a Docker image to completion — on a cron schedule or on demand — and keep a record of every run with its exit code and output. No repository or build step involved.

What a workflow is

A workflow is one Docker image, an optional command, environment variables, an optional cron schedule and a timeout. Each time it fires, HostStack pulls the image, starts a container, waits for it to exit and records the result as a run. Workflows belong to a project and an environment, and their containers join that project's private network, so a run can reach your services on their internal addresses — see Networking.

Use it when
WorkflowThe job is already an image — a vendor tool, curl, a CLI you publish yourself — and should run now and then, finish, and leave a record.
Cron serviceThe job is code in your repository. A cron service is built from the repo like any other service and runs its start command on a schedule — see Cron & uptime.
ServiceThe work is continuous: an HTTP server or a worker that never exits.

Create a workflow

  1. Open Workflows

    In the dashboard sidebar, under Workloads, click Workflows. If your team has more than one project, pick the project first.

  2. Fill in the form

    Click New Workflow and set a Name and a Docker image, such as curlimages/curl:latest. Optionally set a Command, a Schedule in cron syntax (leave it blank for a workflow you only run by hand), a Timeout in seconds, and environment variables as KEY=VALUE lines.

  3. Create it and run it once

    Click Create Workflow, open it, and click Run now to check it works before the schedule fires. The run appears under Recent runs; click it to see its status, timings, exit code and output.

The form splits Command on spaces, so it can't express an argument that contains a space. Use the HTTP API for that — it takes the command as an array. The command replaces the image's CMD; the image's ENTRYPOINT still runs. That is why curlimages/curl, whose entrypoint is curl, takes just the arguments: -fsS https://api.example.com/health.

Trigger a run

A workflow with a schedule fires on it. Schedules are standard 5-field cron expressions (*/5 * * * *), evaluated in UTC; a 6-field expression is read with a leading seconds field. Any workflow — scheduled or not — can also be run by hand with Run now, or through the API. A trigger can pass extra environment variables, which are merged over the workflow's own for that run only:

Trigger a run
curl -X POST \
  https://hoststack.dev/api/teams/$TEAM_ID/workflows/wf_.../trigger \
  -H "Authorization: Bearer hs_live_..." \
  -H "Content-Type: application/json" \
  -d '{"env": {"DRY_RUN": "true"}}'

The response is 202 with the new run, whose publicId starts with wfr_. Poll GET /api/teams/:teamId/workflows/runs/:runId until its status is terminal. There is no CLI command, SDK method or MCP tool for workflows; the dashboard and the HTTP API are the whole surface.

How a run executes

Every fire — scheduled, manual or retry — creates a run in queued and hands it to a HostStack machine. The machine pulls the image, starts the container on the project network, and kills it if it is still running when the timeout is up. The timeout clock starts once the image is pulled.

StatusMeaning
queuedCreated, not yet picked up by a machine.
runningA machine has accepted the run and is pulling the image or running the container.
succeededThe container exited with code 0.
failedThe container exited non-zero, the image couldn't be pulled or started, no machine was available, or the run never reported back (see below). The reason is in the run's error message.
timed_outThe container was still running at timeoutSec and was killed. Its exit code is recorded as -1.
cancelledYou cancelled it with Cancel or the API while it was queued or running.

A run that is still queued or running two minutes after its timeout — because the machine running it went away — is marked failed with the message “Agent did not report completion within timeout window”. That check runs every minute.

The run sandbox

Each run container gets the same sandbox as a cron job: 512 MB of memory, one CPU, at most 256 processes, a read-only root filesystem, and writable /tmp and /var/tmp of 64 MB each that can't hold executables. Write scratch files to /tmp.

Retries

To run a finished run again, open it and click Retry, or call POST /api/teams/:teamId/workflows/runs/:runId/retry. The retry is a new run with trigger retry and its attempt number one higher, and it uses the workflow's current image, command and environment — not any extra variables the original trigger passed. Only a run in a terminal state — succeeded, failed, cancelled or timed_out — can be retried.

Failure notifications

When a run ends failed or timed_out, HostStack sends the workflow.failed event to your team's notification channels — Slack, Discord or email. The message is titled “Workflow run failed” or “Workflow run timed out”, names the workflow with the error message or exit code, and links to the workflow in the dashboard. workflow.failed is one of the critical events a new channel subscribes to by default.

Runs that fail before they start — no machine available — and runs marked failed because they never reported back don't send it. Cancelled runs don't either.

Configuration reference

The body of POST /api/projects/:teamId/:projectId/workflows. PATCH takes any subset of the same fields except projectId and environmentId.

projectId
numberrequired

The project's numeric id. This value, not the one in the URL, decides where the workflow is created, so keep the two the same.

environmentId
number

The environment to run in. Omit it for the project's production environment.

name
stringrequired

1–100 characters, unique among the environment's workflows.

image
stringrequired

An image reference, up to 512 characters, pulled without credentials — so it must be public. ghcr.io/… and other registries work if the image is public.

command
string[] | null

Up to 64 arguments of 1–1,024 characters each. Replaces the image's CMD. null or omitted uses the image's own.

env
Record<string, string>

Names up to 128 characters, values up to 4,096. Stored unencrypted.

schedule
string | null

A cron expression, 5 to 100 characters. null or omitted means manual runs only.

timeoutSec
number

1 to 86,400 (24 hours).

Default: 3600

maxRetries
number

0 to 10. Stored, but not acted on yet — see Retries.

Default: 0

HTTP API

Send an API key as Authorization: Bearer hs_live_.... Reads work for any team member. Everything that changes something needs the member role or above and, with an API key, a key with full access. :workflowId is the workflow's wf_ id and :runId the run's wfr_ id.

Method and pathWhat it does
GET /api/projects/:teamId/:projectId/workflowsList a project's workflows. Paged with page and perPage (default 25); filter with environmentId.
POST /api/projects/:teamId/:projectId/workflowsCreate a workflow. Returns 201.
GET /api/teams/:teamId/workflows/:workflowIdGet one workflow.
PATCH /api/teams/:teamId/workflows/:workflowIdChange any of its fields.
DELETE /api/teams/:teamId/workflows/:workflowIdDelete it. Its schedule stops; a run already in progress is not cancelled.
POST /api/teams/:teamId/workflows/:workflowId/triggerStart a run, with optional { "env": { … } }. Returns 202.
GET /api/teams/:teamId/workflows/:workflowId/runsList its runs, newest first. Paged like the workflow list.
GET /api/teams/:teamId/workflows/runs/:runIdGet one run.
GET /api/teams/:teamId/workflows/runs/:runId/logsGet its stdout and stderr lines.
POST /api/teams/:teamId/workflows/runs/:runId/cancelCancel a queued or running run.
POST /api/teams/:teamId/workflows/runs/:runId/retryStart a new run from a finished one. Returns 202.

trigger and retry are rate limited to 30 requests a minute for each workflow or run, counted per user — or per IP address for an API key.

Next steps

Was this page helpful?

Essential cookies only — for login sessions. No tracking. Details