Nextflow Turret
A self-hosted replacement for Seqera Platform (formerly Nextflow Tower).
Run any Nextflow pipeline with -with-tower, track progress in real time, launch new runs from
a web UI, and inspect logs — all without a cloud subscription.
Features
| Feature | Details |
|---|---|
| Tower trace receiver | Accepts the full Nextflow -with-tower HTTP protocol |
| SQLite persistence | Every run is immediately written to disk; survives restarts |
| Pipeline launcher | Submit pipelines via web form or REST API; cancel running jobs |
| Web dashboard | Live run list, per-run progress detail, failed-task drill-down |
| REST API | JSON API for runs and launches (OpenAPI docs at /docs) |
| Embeddable library | Use WorkflowRegistry + TowerRouter inside your own Python app |
Installation
# Into an existing project
uv add "nextflow-turret @ git+https://github.com/msk-mind/nextflow-turret.git"
# Or with pip
pip install "nextflow-turret @ git+https://github.com/msk-mind/nextflow-turret.git"
Quick start — standalone server
# Start the server (default: http://0.0.0.0:8000, DB: turret.db)
turret
# Or with explicit options (always override config file)
turret --host 127.0.0.1 --port 9000 --db /data/turret.db --log-dir /data/logs
# Or via Python
python -m nextflow_turret.server --port 9000
Config file
Create a turret.toml in the working directory (or ~/.config/turret/config.toml for user-level defaults). CLI flags always win over config values.
[server]
host = "0.0.0.0"
port = 8000
db = "/data/turret.db"
log_dir = "/data/turret-logs"
[launcher]
nextflow = "/opt/nextflow/nextflow"
work_dir = "/scratch/nf-work"
default_profile = "slurm"
A fully-commented template is at turret.toml.example.
Then point Nextflow at it:
nextflow run nf-core/rnaseq -with-tower http://localhost:8000 -name dispatcher_mybatch \
-profile docker -r 3.14.0
Open http://localhost:8000 to see the live dashboard.
REST API
Interactive docs are available at /docs when the server is running.
Tower trace endpoints (called by Nextflow)
| Method | Path | Purpose |
|---|---|---|
GET |
/user-info |
Auth check on Nextflow startup |
POST |
/trace/create |
Workflow registered → returns {workflowId} |
PUT |
/trace/{id}/begin |
Workflow running |
PUT |
/trace/{id}/progress |
Periodic task counts + per-task list |
PUT |
/trace/{id}/heartbeat |
Keepalive (same payload as progress) |
PUT |
/trace/{id}/complete |
Workflow finished |
Runs API
| Method | Path | Description |
|---|---|---|
GET |
/api/runs |
List all runs (newest first) |
GET |
/api/runs/{workflow_id} |
Single run detail with task counts, failures, resources |
Launches API
| Method | Path | Description |
|---|---|---|
GET |
/api/launches |
List all launches (newest first) |
POST |
/api/launches |
Submit a new pipeline launch |
GET |
/api/launches/{id} |
Launch status and metadata |
GET |
/api/launches/{id}/log?tail=N |
stdout/stderr log (optionally tailed) |
DELETE |
/api/launches/{id} |
Cancel a running launch (SIGTERM) |
Submit a launch via REST
curl -X POST http://localhost:8000/api/launches \
-H "Content-Type: application/json" \
-d '{
"pipeline": "nf-core/rnaseq",
"revision": "3.14.0",
"profile": "docker",
"params": {"input": "samplesheet.csv", "genome": "GRCh38"},
"work_dir": "/scratch/work"
}'
run_name → batch_id mapping
Nextflow is launched with -name dispatcher_{batch_id}. The dispatcher_ prefix
is stripped automatically to produce the batch_id that identifies the run.
To use a different prefix, create your own TowerRouter:
from nextflow_turret import TowerRouter, WorkflowRegistry
registry = WorkflowRegistry()
router = TowerRouter(
registry=registry,
run_name_to_batch_id=lambda name: name.removeprefix("mypipeline_"),
)
Embedding in your own Python app
If you prefer to host the Tower receiver inside an existing service rather than running the standalone server, use the core library directly:
from nextflow_turret import TowerRouter, WorkflowRegistry
registry = WorkflowRegistry()
router = TowerRouter(registry=registry)
# In your HTTP handler:
result = router.handle_get(path) # GET /user-info
result = router.handle_post(path, body) # POST /trace/create
result = router.handle_put(path, body) # PUT /trace/{id}/progress
# Read progress at any time:
state = registry.get_by_batch("mybatch")
print(state["pct"], "% complete")
print(state["task_counts"]) # {"succeeded": 42, "running": 3, ...}
print(state["failures"]) # [{"taskId": 7, "process": "ALIGN", "exit": 1}, ...]
Module-level singleton
For quick scripts, a global registry is available:
import nextflow_turret as turret
turret.register_workflow(workflow_id, batch_id, run_name)
turret.update_progress(workflow_id, progress_dict, tasks_list)
turret.mark_complete(workflow_id)
state = turret.get_progress(batch_id) # → dict or None
Utilities
from nextflow_turret import tower_process_to_slurm_name
# Convert a Tower process name to a SLURM job name prefix
tower_process_to_slurm_name("MUSSEL:EXTRACT_FEATURES:TESSELLATE_FEATURIZE_BATCH")
# → "MUSSEL_EXTRACT_FEATURES_TESSELLATE_FEATURIZE_BATCH"
Architecture
nextflow_turret/
├── state.py # WorkflowRegistry, WorkflowState — core in-memory model
├── handlers.py # TowerRouter — parses Tower HTTP payloads
├── db/
│ └── store.py # RunStore — SQLite persistence (runs + launches)
├── launcher/
│ └── launcher.py # Launcher — subprocess management for pipeline runs
└── server/
├── app.py # FastAPI application factory (create_app)
├── registry.py # PersistentWorkflowRegistry — DB-backed registry
├── __main__.py # CLI entry point (turret / python -m nextflow_turret.server)
└── templates/ # Jinja2 HTML templates (dashboard, detail, launch form)
Key design decisions:
- Write-through persistence — every Tower trace event is committed to SQLite before the HTTP response is sent.
- Restart recovery — on startup,
PersistentWorkflowRegistryre-hydrates all rows from the DB so in-progress runs can continue receiving trace events. - No external dependencies for the core library — only
fastapi,uvicorn, andjinja2are needed for the server extras.
Development
git clone https://github.com/msk-mind/nextflow-turret.git
cd nextflow-turret
# Run tests (uv installs deps automatically)
uv run pytest
# Or sync into a venv and work interactively
uv sync --extra server
source .venv/bin/activate
pytest --tb=short -q
Test layout
| File | Tests | Coverage |
|---|---|---|
tests/test_turret.py |
39 | Core library (WorkflowRegistry, WorkflowState, TowerRouter) |
tests/test_server.py |
44 | Server layer (RunStore, PersistentWorkflowRegistry, all endpoints) |
tests/test_integration.py |
32 | Cross-layer: Tower→SQLite, restart recovery, concurrency, API/DB/UI consistency |
tests/test_e2e.py |
34 | Full scenarios: NF trace lifecycle, failed tasks, multi-run dashboard, launch UI journey, error paths |
License
Nextflow Turret is released under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later).
This means:
- Free to use, modify, and self-host for any purpose.
- If you modify Nextflow Turret and run it as a network service, you must make your modified source code available to users of that service.
See LICENSE for the full terms.
Metadata
Release files for nextflow-turret 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nextflow_turret-0.1.0.tar.gz | 74.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nextflow_turret-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 134.3 kB
Release files / nextflow_turret-0.1.0.tar.gz
| Download URL | nextflow_turret-0.1.0.tar.gz |
|---|---|
| Size | 74.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bdfbe11619210a4a23b9651ef0b9728dd2f6a94c3d59bc4d4c0fd7a1bc593858
|
|
BLAKE2b-256 checksum How to use checksums |
a4810df4c8fda5a2a65d7985d5bb5d73188998e00721daa66adc8e718cbf27cf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.
Transparency logRelease files / nextflow_turret-0.1.0-py3-none-any.whl
| Download URL | nextflow_turret-0.1.0-py3-none-any.whl |
|---|---|
| Size | 59.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5a166d370c1b174afe466a5125f91e967cef3aae9782357ec57a532bbdee8d3
|
|
BLAKE2b-256 checksum How to use checksums |
85e2cdd4f136cd00990046ce1f945f646cab43f92aea239a3b83ca9cc77109ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 2, 2026.
Transparency log