This guide covers deploying NodeTool on your own infrastructure.
Overview
Self-hosted deployment runs NodeTool in a Docker container — on localhost
or on a remote host reached over SSH. Docker is the only supported deployment
type (SUPPORTED_TYPES = ["docker"]).
Two paths:
- Docker Compose — a single
docker-compose.ymlyou run yourself. The fastest way to stand up one server. See below. nodetool deployCLI — a managed flow driven bydeployment.yamlthat also handles remote hosts over SSH, image transfer, and workflow sync. See Deployment Configuration.
Docker Compose (reference)
The repository ships a reference
docker-compose.yml
for running one server on a host you control.
cp .env.example .env # fill in the provider keys you use
docker compose up -d
# open http://localhost:17777
The server binds to 0.0.0.0:7777 inside the container and is published on the
host as ${NODETOOL_PORT:-17777}. All persistent state — SQLite database,
assets, vector store, model cache, and the generated secret key — lives under
/workspace, backed by the named nodetool-data volume, so it survives
restarts and image upgrades.
Common overrides (set in .env or the shell):
| Variable | Default | Purpose |
|---|---|---|
NODETOOL_VERSION |
latest |
Image tag to pull (pin a release in production) |
NODETOOL_PORT |
17777 |
Host port mapped to the container’s 7777 |
NODETOOL_TRUST_LOCAL_NETWORKS |
172.16.0.0/12 |
⚠️ Source CIDRs trusted as user 1 without a login (Local mode). Docker bridge by default — never 0.0.0.0/0 on a public IP. Ignored in Supabase mode |
SECRETS_MASTER_KEY |
auto-generated | 32-byte base64 key encrypting stored secrets — set explicitly in production (openssl rand -base64 32) |
OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, FAL_API_KEY, HF_TOKEN |
unset | Model provider keys |
Upgrade in place:
docker compose pull
docker compose up -d
To store data in a host directory instead of the named volume, replace the
nodetool-data:/workspace mount with a bind mount (e.g. ./nodetool-data:/workspace)
and make sure the host directory is writable by the container’s node user.
Authentication / login screen
Auth is configured entirely on the backend. The web UI fetches its auth mode
and public Supabase credentials at runtime from GET /api/config (a public,
non-secret endpoint), so the same frontend build works with or without login —
no rebuild, and it works even when the frontend is served from a different
origin.
- Login off (default). With
SUPABASE_URL/SUPABASE_KEYunset the server runs in Local mode: it trusts requests by source IP (loopback, plus anyNODETOOL_TRUST_LOCAL_NETWORKSyou set) and runs them as a single user; other requests are rejected, and the UI shows no login screen. In Docker the bundled Compose file trusts the Docker bridge (172.16.0.0/12) so a local install works out of the box — see the warning below. -
Login on. Set these three on the server to switch to Supabase mode — the server requires a valid Supabase JWT on every request and the UI shows the login screen:
SUPABASE_URL=https://your-project.supabase.co SUPABASE_KEY=your-service-role-key # server-only, never sent to the browser SUPABASE_ANON_KEY=your-anon-key # public key the login screen usesOptionally set
AUTH_REDIRECT_URLwhen serving behind a domain/proxy (it must be in the Supabase project’s redirect allow list).
GET /api/config returns authMode, supabaseUrl, supabaseAnonKey,
authRedirectUrl, and version — never the service-role key. See
Authentication and Supabase Deployment.
🔒 Do not expose Local mode to the internet
Local mode has no login.
NODETOOL_TRUST_LOCAL_NETWORKStrusts a set of source IPs as admin user"1"with no password — full access to your data, secrets, and API keys. Anyone who can reach the published port from a trusted range gets in.
- Safe on a laptop or a private LAN/VPN behind a firewall.
- The default
172.16.0.0/12trusts only Docker’s bridge, not the wider internet. Never change it to0.0.0.0/0on a public IP.- Putting NodeTool on a public address or sharing it with untrusted users? Enable Supabase mode (above) so every request needs a real login, and terminate TLS in front of the server.
Serving the web UI from a different origin (e.g. a CDN)? Point the frontend at the backend with the build-time
VITE_API_URL, and add that origin to the server’sNODETOOL_ALLOWED_ORIGINSso the cross-originGET /api/configand API calls are permitted. Everything else still comes from/api/config.
Deployment Configuration
Deployments are configured via deployment.yaml.
Docker Deployment
deployments:
my-server:
type: docker
enabled: true
host: 192.168.1.10
ssh:
user: ubuntu
key_path: ~/.ssh/id_rsa
container:
name: nodetool-server
port: 8000
gpu: "0"
paths:
workspace: /data/nodetool
hf_cache: /data/hf-cache
image:
name: ghcr.io/nodetool-ai/nodetool
tag: latest
For a local host, set host: localhost and omit the ssh block.
Apply Flow
- Directory Creation: Ensures
workspaceandhf_cachedirectories exist. - Image Check: Verifies the configured image exists locally/remote.
deploy applydoes not auto-pull. - Image Transfer: For remote hosts, copies image to remote runtime if needed.
- Container Management: Restarts container with new configuration.
- Health Check: Verifies HTTP endpoint.
End-to-End: Local Docker Deployment
This walkthrough matches a common local setup flow:
- Pull the Docker image.
- Add a docker deployment interactively.
- Review the generated deployment.
- Apply deployment and validate health.
- Sync workflows.
- Run a synced workflow on the deployed instance.
0. Pull the Image First
docker pull ghcr.io/nodetool-ai/nodetool:latest
1. Add Local Docker Deployment
nodetool deploy add local --type docker
--type docker is required. The command then prompts for the rest:
- Host address:
localhost - Docker image name:
ghcr.io/nodetool-ai/nodetool - Docker image tag:
latest - Container name:
nodetool - Port:
8000 - GPU/workflows assignment: optional
- Workspace folder:
$HOME/.nodetool-workspace - HF cache folder: canonical local HF cache (auto-detected, usually
$HOME/.cache/huggingface/hub)
Path meanings:
- Workspace: stores assets and temporary runtime data.
- HF cache: stores downloaded Hugging Face artifacts/models.
2. Review Deployment Config
nodetool deploy show local
This dumps the deployment entry as YAML. You should see something like:
local:
type: docker
host: localhost
image:
name: ghcr.io/nodetool-ai/nodetool
tag: latest
container:
name: nodetool
port: 8000
paths:
workspace: <your workspace path>
hf_cache: <your HF cache path>
The server endpoint is at http://localhost:8000. The container EXPOSEs 7777;
when container.port is 7777 the deployer maps it to host port 8000 (any
other container.port is used as-is).
You can also inspect the raw config:
cat ~/.config/nodetool/deployment.yaml
To change a field deploy add did not prompt for, open the file in $EDITOR:
nodetool deploy edit
deploy edit takes no deployment name — it opens the whole deployment.yaml.
Without one on disk it stops and tells you to run nodetool deploy init first.
3. Apply Deployment
nodetool deploy apply local
Expected successful output includes:
- directories created
- image check passed
- app container started
- health checks passing for
http://127.0.0.1:8000/health Deployment successful- secrets synced
If the first apply fails (for example due to container/port conflicts), run apply again:
nodetool deploy apply local
Then confirm runtime state:
nodetool deploy status local
docker ps --filter name=nodetool --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
curl http://127.0.0.1:8000/health
4. Sync Workflows to the Deployment
List local workflows first:
nodetool workflows list
Sync one workflow by ID to the deployed instance:
nodetool deploy workflows sync local <workflow_id>
This sync command also:
- uploads referenced assets
- downloads referenced models (HuggingFace/Ollama) on the target
Verify remote workflows:
nodetool deploy workflows list local
5. Test Workflow Execution on Deployed Instance
Run a synced workflow remotely:
nodetool deploy workflows run local <workflow_id>
Optional params example:
nodetool deploy workflows run local <workflow_id> -p prompt="hello"
If a run fails, inspect logs:
nodetool deploy logs local --tail 200
API Users
A deployment that serves more than one person needs API users. Four
nodetool deploy subcommands manage them over the running server’s admin API,
so the deployment must already be applied and reachable.
Every one of them needs an admin bearer token. It comes from --token, else
from NODETOOL_ADMIN_TOKEN, else from an interactive prompt — with no TTY and
neither set, the command exits with
Admin token required. Provide --token or set NODETOOL_ADMIN_TOKEN.
users-add <deployment> <username>— create a user and print its token.--role <admin|user>defaults touser; any other value is refused. The token is printed once and never again.users-list <deployment>— username, user id, role, a 16-character preview of the stored token hash, and creation time.--jsonprints the raw records.users-remove <deployment> <username>— delete a user. Prompts first;--forceskips the prompt.users-reset-token <deployment> <username>— issue a new token and invalidate the old one. Likeusers-add, the new token is shown once.
Examples:
export NODETOOL_ADMIN_TOKEN=<admin token>
nodetool deploy users-add local alice --role admin
nodetool deploy users-list local
nodetool deploy users-list local --json
nodetool deploy users-reset-token local alice
nodetool deploy users-remove local alice --force
Manual Troubleshooting
Container Logs
nodetool deploy logs local --tail 200
For a remote host you can also read the container logs directly:
ssh user@host "docker logs nodetool-server"