Quickstart
This guide gets you from zero to running your first workflow in under five minutes using Docker Compose.
Prerequisites
Section titled “Prerequisites”- Docker and Docker Compose
curlandjq(for API calls)
Start the stack
Section titled “Start the stack”# Clone the repositorygit clone https://github.com/fremvaerk/stroem.gitcd stroem
# Start all services (Postgres, server, worker)docker compose up -d
# Wait for the server to be readyuntil curl -sf http://localhost:8080/api/workspaces/default/tasks >/dev/null; do sleep 2; doneThis starts three containers:
- PostgreSQL — stores jobs, steps, and worker state
- Server — HTTP API, workflow engine, embedded web UI
- Worker — polls for work and executes steps
Run your first workflow
Section titled “Run your first workflow”The default workspace includes a hello-world task. Let’s trigger it:
# List available taskscurl -s http://localhost:8080/api/workspaces/default/tasks | jq .
# Run the hello-world taskcurl -s -X POST http://localhost:8080/api/workspaces/default/tasks/hello-world/execute \ -H "Content-Type: application/json" \ -d '{"input": {"name": "World"}}' | jq .The response contains a job_id. Use it to check status and view logs:
# Check job status (replace JOB_ID)curl -s http://localhost:8080/api/jobs/JOB_ID | jq .
# View logscurl -s http://localhost:8080/api/jobs/JOB_ID/logs | jq -r .logsOpen the web UI
Section titled “Open the web UI”Visit http://localhost:8080 to see the web UI. From there you can:
- Browse tasks and trigger them with a form
- Watch jobs execute in real-time with live log streaming
- View step dependencies as an interactive graph
Explore the workflow file
Section titled “Explore the workflow file”The hello-world task is defined in workspace/.workflows/hello.yaml:
actions: greet: type: script script: "echo Hello {{ input.name }} && echo 'OUTPUT: {\"greeting\": \"Hello {{ input.name }}\"}'" input: name: { type: string, required: true }
shout: type: script script: "echo {{ input.message }} | tr '[:lower:]' '[:upper:]'" input: message: { type: string, required: true }
tasks: hello-world: mode: distributed input: name: { type: string, default: "World" } flow: say-hello: action: greet input: name: "{{ input.name }}" shout-it: action: shout depends_on: [say-hello] input: message: "{{ say_hello.output.greeting }}"Key concepts:
- Actions define reusable commands or scripts
- Tasks compose actions into a DAG of steps
- Steps pass data via
OUTPUT: {json}lines in stdout - Templates use Tera syntax (
{{ variable }})
Clean up
Section titled “Clean up”docker compose down -vArchitecture
Section titled “Architecture” ┌─────────────────┐ │ PostgreSQL │ └────────┬────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ Server │ │ Worker │ │ CLI │ │ (Axum) │◄──┤ (Poll) │ │(reqwest)│ └────┬────┘ └────┬────┘ └─────────┘ │ │ ┌────────┼────────┐ │ │ Public │ Worker │ ├──── ShellRunner (host) │ API │ API │ ├──── DockerRunner (bollard) └────────┴────────┘ └──── KubeRunner (kube)- Server: Loads workflows from workspaces, manages jobs and steps in PostgreSQL, orchestrates DAG execution, streams logs via WebSocket, serves the embedded web UI.
- Worker: Polls the server for ready steps, dispatches to the appropriate runner (script, Docker, or Kubernetes), streams logs back.
- CLI: HTTP client for triggering tasks, checking status, validating workflows, and viewing logs.
Next steps
Section titled “Next steps”- Configuration — server and worker config files
- Workflow Basics — YAML structure in depth
- Action Types — script, Docker, Kubernetes, and task actions