starlark-dev
$
npx mdskill add kurtosis-tech/kurtosis/starlark-devDebug and build Kurtosis Starlark packages locally.
- Creates packages from scratch using kurtosis.yml and main.star files.
- Requires kurtosis CLI for running and testing local packages.
- Executes code in two phases: planning and service configuration.
- Delivers results by running packages with or without dry-run mode.
SKILL.md
.github/skills/starlark-devView on GitHub ↗
---
name: starlark-dev
description: Develop and debug Kurtosis Starlark packages. Create packages from scratch, understand the plan-based execution model, use print() debugging, handle future references, and test packages locally. Use when writing or troubleshooting .star files.
compatibility: Requires kurtosis CLI.
metadata:
author: ethpandaops
version: "1.0"
---
# Starlark Dev
Create, debug, and test Kurtosis Starlark packages.
## Package structure
A minimal Kurtosis package needs two files:
```
my-package/
kurtosis.yml # Package metadata
main.star # Entry point
```
### kurtosis.yml
```yaml
name: github.com/your-org/my-package
```
### main.star
```python
def run(plan, args):
plan.add_service(
name="my-service",
config=ServiceConfig(
image="nginx:latest",
ports={
"http": PortSpec(number=80, transport_protocol="TCP"),
},
),
)
```
## Running packages
```bash
# Run a local package
kurtosis run ./my-package
# Run with parameters
kurtosis run ./my-package '{"param1": "value1"}'
# Run a remote package from GitHub
kurtosis run github.com/ethpandaops/ethereum-package
# Run with a custom config file
kurtosis run github.com/ethpandaops/ethereum-package --args-file config.yaml
# Dry run (plan only, no execution)
kurtosis run ./my-package --dry-run
```
## Execution model
Kurtosis Starlark executes in two phases:
1. **Planning phase** — Your code runs and builds a plan of actions. `add_service()`, `exec()`, etc. don't execute immediately — they return future references.
2. **Execution phase** — The plan is executed in order. Future references are resolved to actual values.
This means you **cannot** use the return value of `plan.exec()` in Python-level logic like `if/else` during the planning phase. Use `plan.verify()` or `plan.assert()` instead.
```python
# WRONG: result is a future reference, not a real value during planning
result = plan.exec(service_name="my-service", recipe=ExecRecipe(command=["echo", "hello"]))
if result["output"] == "hello": # This won't work as expected
plan.print("matched")
# RIGHT: use plan.verify for conditional checks
result = plan.exec(service_name="my-service", recipe=ExecRecipe(command=["echo", "hello"]))
plan.verify(result["exit_code"], "==", 0)
```
## Debugging with print
```python
def run(plan, args):
plan.print("Args received: {}".format(args))
service = plan.add_service(
name="my-service",
config=ServiceConfig(image="nginx:latest"),
)
plan.print("Service IP: {}".format(service.ip_address))
plan.print("Service hostname: {}".format(service.hostname))
```
## Common patterns
### Wait for service readiness
```python
plan.wait(
service_name="my-service",
recipe=GetHttpRequestRecipe(port_id="http", endpoint="/health"),
field="code",
assertion="==",
target_value=200,
timeout="60s",
)
```
### Execute commands in a service
```python
result = plan.exec(
service_name="my-service",
recipe=ExecRecipe(command=["cat", "/etc/hostname"]),
)
plan.verify(result["exit_code"], "==", 0)
plan.print("Hostname: {}".format(result["output"]))
```
### Upload files
```python
config_template = read_file("./templates/config.toml")
artifact = plan.render_templates(
name="my-config",
config={
"config.toml": struct(
template=config_template,
data={"key": "value"},
),
},
)
plan.add_service(
name="my-service",
config=ServiceConfig(
image="my-image:latest",
files={"/etc/myapp": artifact},
),
)
```
### Import from other packages
```python
dependency = import_module("github.com/org/other-package/lib.star")
def run(plan, args):
dependency.some_function(plan)
```
## Testing
Use a dry-run → execute → verify workflow:
```bash
# 1. Validate the plan without executing
kurtosis run --dry-run ./my-package
# 2. Run and check output
kurtosis run ./my-package
# 3. Inspect the created enclave
kurtosis enclave inspect <enclave-name>
# 4. Check service logs
kurtosis service logs <enclave-name> <service-name>
# 5. Shell into a service to verify state
kurtosis service shell <enclave-name> <service-name>
# 6. Clean up after testing
kurtosis clean -a
```
## Common errors
| Error | Cause | Fix |
|-------|-------|-----|
| `cannot use future reference in if` | Using plan result in Python logic | Use `plan.verify()` or `plan.assert()` |
| `service not found` | Service name typo or not yet created | Check `plan.add_service()` name matches |
| `port not found` | Port ID mismatch | Ensure `port_id` in recipes matches `ports` dict key |
| `image pull failed` | Image doesn't exist or no auth | Verify image tag, check `docker pull` manually |
| `kurtosis.yml not found` | Running from wrong directory | Run from package root containing `kurtosis.yml` |
More from kurtosis-tech/kurtosis
- cli-local-buildBuild and test the Kurtosis CLI from source. Compile the CLI binary locally, run it against Docker or Kubernetes engines, and iterate on CLI changes without creating a release. Use when developing or debugging CLI commands.
- cluster-manageManage Kurtosis cluster settings. Switch between Docker and Kubernetes backends, list available clusters, and configure which cluster Kurtosis uses. Use when you need to change where Kurtosis runs enclaves.
- context-manageManage Kurtosis contexts for connecting to different Kurtosis instances. Add, list, switch, and remove contexts. Use when working with multiple Kurtosis environments (local, remote, team shared).
- docker-debugDebug Kurtosis running on local Docker. Inspect engine, API container, and service logs. Diagnose container crashes, port conflicts, and networking issues. Use when kurtosis commands fail or services aren't reachable on Docker.
- docker-local-buildBuild and test Kurtosis from source on local Docker. Compiles all components (engine, core, files-artifacts-expander), builds Docker images, installs the CLI, and restarts the engine. Use when developing Kurtosis and testing changes locally with Docker.
- dumpDump Kurtosis state for debugging and sharing. Export enclave state including service logs, configurations, and file artifacts to a local directory. Use when you need to capture state for offline analysis or to share with others for debugging.
- enclave-inspectInspect and manage Kurtosis enclaves. List enclaves, view services and ports, examine file artifacts, dump enclave state for debugging, and clean up. Use when you need to understand what's running inside an enclave or export its state.
- engine-manageManage the Kurtosis engine server. Start, stop, restart the engine, check status, and view engine logs. Covers both Docker and Kubernetes engine backends. Use when the engine won't start, needs restarting, or you need to check engine health.
- files-inspectInspect, download, upload, and debug Kurtosis file artifacts. View artifacts in an enclave, download them locally for inspection, upload local files, and troubleshoot file mounting issues. Use when services can't find expected files or configs are wrong.
- gatewayStart and manage the Kurtosis gateway for Kubernetes. The gateway forwards local ports to the Kurtosis engine and services running in a k8s cluster. Required when using Kurtosis with Kubernetes. Use when kurtosis engine status shows nothing on k8s or services aren't reachable.