From northflank
Deploys and manages infrastructure on Northflank: projects, services, jobs, databases, secrets, domains, environments, pipelines, and templates. Use when interacting with the Northflank API, CLI, or JS client.
How this skill is triggered — by the user, by Claude, or both
Slash command
/northflank:northflankThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Note:** CLI examples in this file (and the other hand-maintained references) are checked against `northflank <verb> <noun> --help`, which only confirms that flags and subcommands exist — not that the command behaves as documented at runtime. Smoke-test before relying on a snippet in production.
references/api-overview.mdreferences/api/_index.mdreferences/api/copy-files.mdreferences/api/execute-command.mdreferences/api/forwarding.mdreferences/api/introduction.mdreferences/api/log-tailing.mdreferences/api/miscellaneous/_index.mdreferences/api/miscellaneous/list-plans.mdreferences/api/miscellaneous/list-regions.mdreferences/api/org/_index.mdreferences/api/org/billing/_index.mdreferences/api/org/billing/get-invoice.mdreferences/api/org/billing/get-usage.mdreferences/api/org/billing/list-invoices.mdreferences/api/org/billing/list-usage.mdreferences/api/org/cloud-providers/_index.mdreferences/api/org/cloud-providers/cordon-cluster-node.mdreferences/api/org/cloud-providers/delete-cluster.mdreferences/api/org/cloud-providers/delete-integration.mdNote: CLI examples in this file (and the other hand-maintained references) are checked against
northflank <verb> <noun> --help, which only confirms that flags and subcommands exist — not that the command behaves as documented at runtime. Smoke-test before relying on a snippet in production.
Northflank is a developer platform for building, deploying, and scaling workloads on Kubernetes — on Northflank Cloud, your own cloud (BYOC), or your own clusters (BYOK). It handles build, deploy, release, auto-scaling, and disaster recovery across AWS, GCP, Azure, CoreWeave, and on-prem. Core primitives: projects (Kubernetes namespaces), services (stateless workloads), add-ons (managed databases), jobs (cron/manual), secret groups, volumes, and templates (IaC/GitOps). Everything in the UI is also available via the REST API, CLI, and JS client.
Base API URL: https://api.northflank.com/v1/
Auth: Authorization: Bearer <token>
Rate limit: 1000 req/hr (x-ratelimit-remaining header)
Before writing any Northflank code, verify setup:
CLI or JS client? Pick the interface that matches who's doing the work:
northflank login session, doesn't require an extra token in the environment, and keeps one-off operations auditable.@northflank/js-client) or REST API when the user is building a feature, product, or automation on top of Northflank — i.e. code that will run in their app, script, or service. That's when you want a programmatic SDK, typed responses, retries, and an NF_API_TOKEN baked into their deployment.CLI installed? Check northflank --help. If missing:
npm install -g @northflank/cli
# or
yarn global add @northflank/cli
JS client installed? Check package.json for @northflank/js-client. If missing:
npm install @northflank/js-client
# or
yarn add @northflank/js-client
API token set? Check the environment for NF_API_TOKEN (or equivalent). If not set, tell the user to create one from their team page: Team page → Team settings (top right) → API → Tokens → Create API token. They also need an API role with appropriate permissions under Team settings → API → Roles.
Project ID known? Most operations require a projectId. It's the slug of the project name (e.g. "My Project" → my-project). If using the CLI, set a default with northflank context use project or pass it explicitly. If using the JS client, list projects with apiClient.list.projects({}).
Critical: Before performing any destructive operation on Northflank, stop and explicitly ask the user to confirm. Do not assume prior approval transfers across operations or sessions — confirm each one individually.
Destructive operations include, at minimum:
delete service / apiClient.delete.service — irreversible; loses container state and any service-scoped configurationdelete addon / apiClient.delete.addon — EXTRA CARE: addons hold persistent data (databases, queues, caches). Deletion is irreversible and destroys all data unless the user has a recent backup. Always confirm the addon name back to the user, ask whether a backup exists, and do not proceed without an explicit "yes, delete <addon-id>."delete volume — destroys persistent disk contentsdelete project — wipes the entire namespace and every resource in itdelete secret — may break running workloads that depend on those keysdelete job — removes the job and its run historydelete template / running a template that destroys resourcesdelete domain / delete dns-recorddelete cluster (BYOC/BYOK) — affects every workload on that clusterpatch / update that shrinks persistent state (e.g. lowering replica count on an addon — Northflank rejects this anyway, but never attempt it as a workaround)Confirmation rules:
pause service instead of delete service, snapshot/backup an addon before deleting, etc.npm i -g @northflank/cli
northflank login
northflank context ls
northflank context use
northflank context use project
northflank login creates a context and can open a browser to select or create a token.northflank context use project|service|job to set defaults so later commands can omit repeated IDs.northflank command-overview to see the command tree.# Prompts for missing values interactively
northflank get service
# Explicit project and service
northflank get service --projectId <PROJECT_ID> --serviceId <SERVICE_ID>
The CLI is interactive by default. If IDs are omitted it will usually prompt for them.
# Interactive shell session
northflank exec service
# One-off command
northflank exec service --cmd "ls -lah /app"
# Run as a specific user
northflank exec service --user root --cmd id
Use northflank exec job for jobs instead of services.
sudo northflank forward service --projectId <PROJECT_ID> --serviceId <SERVICE_ID>
sudo northflank forward addon --projectId <PROJECT_ID> --addonId <ADDON_ID>
# If sudo path/context resolution causes issues
sudo --preserve-env=PATH,HOME bash -c 'northflank forward service --projectId <PROJECT_ID> --serviceId <SERVICE_ID>'
--skipHostnames if rootless forwarding is acceptable and only IP/port access is needed.northflank forward all --projectId <PROJECT_ID> to open all project tunnels at once.northflank create project --help
northflank create project --file ./project.yaml
CLI resource definitions follow the same shape as the API request bodies.
import { ApiClient, ApiClientInMemoryContextProvider } from '@northflank/js-client';
const contextProvider = new ApiClientInMemoryContextProvider();
await contextProvider.addContext({
name: 'default',
token: process.env.NF_API_TOKEN,
});
// Pass true as second arg to throw on HTTP errors
const apiClient = new ApiClient(contextProvider, true);
All methods follow the pattern:
apiClient.{verb}.{resource}({ parameters, data, options })
parameters — path params (e.g. projectId, serviceId)data — request body (for create/update)options — query params (for filtering, pagination)const result = await apiClient.create.service.deployment({
parameters: { projectId: 'my-project' },
data: {
name: 'my-api',
billing: { deploymentPlan: 'nf-compute-10' },
deployment: {
instances: 1,
external: { imagePath: 'nginx:latest' },
docker: { configType: 'default' },
},
ports: [{ name: 'http', internalPort: 80, public: true, protocol: 'HTTP' }],
},
});
const serviceId = result.data.id; // 'my-api'
Common plan sizes: nf-compute-10 (0.1 vCPU/256MB), nf-compute-50 (0.5/1GB), nf-compute-100-2 (1/2GB), nf-compute-200 (2/4GB). See Compute & GPU Plans for the full table, GPU SKUs, and how to query the live list.
await apiClient.create.service.combined({
parameters: { projectId: 'my-project' },
data: {
name: 'my-app',
billing: { deploymentPlan: 'nf-compute-50' },
vcsData: {
projectUrl: 'https://github.com/org/repo',
projectType: 'github',
projectBranch: 'main',
},
buildSettings: {
dockerfile: {
buildEngine: 'buildkit',
dockerFilePath: '/Dockerfile',
dockerWorkDir: '/',
},
},
deployment: { instances: 1 },
ports: [{ name: 'app', internalPort: 3000, public: true, protocol: 'HTTP' }],
},
});
// List all services in a project
const { data } = await apiClient.list.services({ parameters: { projectId: 'my-project' } });
const services = data.services;
// Get a single service
const svc = await apiClient.get.service({
parameters: { projectId: 'my-project', serviceId: 'my-api' },
});
// Deployment rollout state: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED'
// ('COMPLETED' = rolled out and serving, NOT "exited")
console.log(svc.data.status.deployment?.status);
// Build state (only present for combined/build services):
// 'QUEUED' | 'PENDING' | 'STARTING' | 'BUILDING' | 'SUCCESS' | 'FAILURE' | ...
console.log(svc.data.status.build?.status);
// Pause is a separate boolean — not encoded in either status above
console.log(svc.data.servicePaused);
// Short-lived command — returns stdout/stderr/exitCode
const result = await apiClient.exec.execServiceCommand(
{ projectId: 'my-project', serviceId: 'my-api' },
{ command: ['ls', '-lah', '/app'] },
);
console.log(result.stdOut);
console.log(result.commandResult.exitCode); // 0 = success
// Long-running / interactive session
const { exec } = apiClient;
const session = await exec.execServiceSession(
{ projectId: 'my-project', serviceId: 'my-api' },
{ shell: 'bash' },
);
session.stdErr.on('data', (chunk) => console.error(chunk));
session.stdIn.write('echo hello\n');
const result2 = await session.waitForCommandResult();
For more command-execution details, see references/api/execute-command.md.
const logsClient = await apiClient.get.service.logTail({
parameters: { projectId: 'my-project', serviceId: 'my-api' },
options: { lineLimit: 20 },
});
logsClient.on('logs-received', (lines) => {
lines.forEach((l) => console.log(`[${l.ts.toISOString()}] ${l.log}`));
});
logsClient.on('error', console.error);
await logsClient.start();
// Call await logsClient.stop() when done
const params = { parameters: { projectId: 'my-project', serviceId: 'my-api' } };
await apiClient.pause.service(params); // stops billing, keeps config
await apiClient.resume.service(params); // restarts from paused
await apiClient.restart.service(params); // rolling restart (keeps running)
await apiClient.delete.service(params); // permanent, irreversible
// Update image or instance count
await apiClient.patch.service.deployment({
parameters: { projectId: 'my-project', serviceId: 'my-api' },
data: {
deployment: {
instances: 3,
external: { imagePath: 'myregistry/myapp:v2' },
},
},
});
const { data } = await apiClient.create.addon({
parameters: { projectId: 'my-project' },
data: {
name: 'my-postgres',
type: 'postgresql', // postgresql | mongodb | mysql | redis | rabbitmq | minio | memcached
version: '16',
billing: {
deploymentPlan: 'nf-compute-10',
storage: 4096, // MB
replicas: 1,
},
tlsEnabled: true,
},
});
const addonId = data.id; // 'my-postgres'
const creds = await apiClient.get.addon.credentials({
parameters: { projectId: 'my-project', addonId: 'my-postgres' },
});
// creds.data.username, creds.data.password, creds.data.connectionString, etc.
console.log(creds.data.connectionString);
Secret groups link add-on connection strings to service env vars, and/or hold arbitrary key-value secrets.
// Create a secret group with a static env var and a linked addon credential
await apiClient.create.secret({
parameters: { projectId: 'my-project' },
data: {
name: 'app-secrets',
secretType: 'environment', // 'environment' | 'build-arg' | 'global'
priority: 10,
data: {
NODE_ENV: 'production',
API_KEY: 'supersecret',
},
addonDependencies: [
{
addonId: 'my-postgres',
keys: [
{ keyName: 'POSTGRES_URI', aliases: ['DATABASE_URL'] },
],
},
],
restrictions: {
restricted: true,
nfObjects: [{ id: 'my-api', type: 'service' }],
},
},
});
// List secrets in project
const { data } = await apiClient.list.secrets({ parameters: { projectId: 'my-project' } });
// Update a secret group (add/change key)
await apiClient.patch.secret({
parameters: { projectId: 'my-project', secretId: 'app-secrets' },
data: { data: { FEATURE_FLAG: 'true' } },
});
List endpoints return 50 items by default. Two options:
// Fetch all pages automatically (multiple API calls)
const all = await apiClient.list.services.all({ parameters: { projectId: 'my-project' } });
// Manual next-page
const page1 = await apiClient.list.services({ parameters: { projectId: 'my-project' } });
if (page1.pagination?.hasNextPage) {
const page2 = await page1.pagination.getNextPage();
}
// Option 1: check result.error (client does NOT throw by default)
const result = await apiClient.get.service({ parameters: { projectId, serviceId } });
if (result.error) {
console.error(result.error.status, result.error.message);
}
// Option 2: init client with throwOnError = true
const apiClient = new ApiClient(contextProvider, true);
try {
await apiClient.get.service({ parameters: { projectId, serviceId } });
} catch (err) {
console.error(err);
}
const { rawResponse } = await apiClient.get.service({ parameters: { projectId, serviceId } });
const remaining = rawResponse.headers.get('x-ratelimit-remaining');
const reset = rawResponse.headers.get('x-ratelimit-reset'); // seconds
Full live tables (every compute plan, GPU SKU, pricing, and per-region GPU availability) are auto-generated into references/plans.md by scripts/generate_references.js. Re-run with --force to refresh after Northflank ships new SKUs or price changes. The underlying endpoints are public — no auth needed:
curl -s https://api.northflank.com/v1/plans | jq '.data.plans[] | {id, cpu: .cpuResource, ramMB: .ramResource, hr: .amountPerHour}'
curl -s https://api.northflank.com/v1/regions | jq '.data.regions[] | {id, gpus: (.gpuDevices // [] | map(.id))}'
JS client / CLI equivalents: apiClient.list.plans({}) / apiClient.list.regions({}) and northflank list plans / northflank list regions.
Format: nf-compute-<cpu*100>-<ram_gb> (newer, explicit) or nf-compute-<cpu*100> (legacy, RAM implied). Common picks:
nf-compute-10 — 0.1 vCPU / 256 MB (~$2.70/mo) — sidecars, light workersnf-compute-50 — 0.5 vCPU / 1 GB (~$12/mo) — small APIs, cron jobsnf-compute-200 — 2 vCPU / 4 GB (~$48/mo) — typical production servicenf-compute-400-16 — 4 vCPU / 16 GB (~$144/mo) — also valid as buildPlanbuildPlan only accepts plans with 4+ vCPU and defaults to nf-compute-400-16 if omitted. See references/plans.md for all 20 SKUs.
Format: nf-gpu-<gpuType>-<count>g (the g suffix is literal, not a unit). The plan bundles CPU/RAM around the GPU — you do not combine an nf-compute-* plan with a separate GPU. Worked example:
data: {
billing: { deploymentPlan: 'nf-gpu-a100-80-1g' }, // 1× A100 80GB
deployment: {
gpu: { enabled: true, gpuType: 'a100-80', gpuCount: 1 },
// ...
},
}
gpuType is the model id (lowercase, no nvidia- prefix). Currently: l4-24, a100-40, a100-80, h100-80, h200-141, b200-180.gpuCount must be one of the model's countOptions (typically 1, 2, 4, 8; H200 and B200 are 8-only). Invalid counts are rejected.gpu block can sit under deployment.gpu or billing.gpu; Northflank's own templates use deployment.gpu.For BYOC clusters, GPU node types come from the cloud provider — query with apiClient.list.cloudProviders.nodeTypes({ options: { hasGpu: true } }) and define custom resource plans (see references/guides/bring-your-own-cloud.md#create-custom-resource-plans). Timeslicing is supported on BYOC, not on managed cloud.
For Northflank-published GPU base images (PyTorch + CUDA + Jupyter pre-installed), pull from europe-docker.pkg.dev/northflank/public/... instead of building from raw pytorch/pytorch:*.
await apiClient.create.service.deployment({ parameters: { projectId }, data: { ...serviceSpec } });
// Poll until the deployment has rolled out
let status;
do {
await new Promise((r) => setTimeout(r, 2000));
const svc = await apiClient.get.service({ parameters: { projectId, serviceId: 'my-api' } });
status = svc.data.status.deployment?.status;
if (status === 'FAILED') throw new Error('Deployment failed');
} while (status !== 'COMPLETED');
data: {
deployment: {
instances: 1,
autoscaling: {
horizontal: {
enabled: true,
minReplicas: 1,
maxReplicas: 10,
cpu: { enabled: true, thresholdPercentage: 70 },
rps: { enabled: true, thresholdValue: 500 },
},
},
},
}
await apiClient.run.template({
parameters: { templateId: 'my-template' },
data: {
arguments: {
REGION: 'europe-west',
IMAGE: 'myapp:latest',
},
},
});
await apiClient.create.job({
parameters: { projectId: 'my-project' },
data: {
name: 'db-migrate',
billing: { deploymentPlan: 'nf-compute-10' },
deployment: {
external: { imagePath: 'myapp:latest' },
docker: { configType: 'default' },
},
settings: {
cron: { schedule: '0 2 * * *' }, // present = cron job
concurrencyPolicy: 'Forbid',
},
runtimeEnvironment: { MIGRATE: 'true' },
},
});
// Run it manually now
await apiClient.start.job.run({ parameters: { projectId: 'my-project', jobId: 'db-migrate' } });
"My App" becomes my-api (slug). Use IDs in API calls, not display names.x-ratelimit-remaining.put.* methods use upsert semantics — create if missing, update if present. Useful for idempotent IaC.northflank exec --cmd "..." swallows output without a TTY — in CI, agent harnesses, or anything wrapped in bash -c, stdout/stderr are silently discarded. Wrap with script -q /dev/null northflank exec ... (macOS/BSD) or script -qfc 'northflank exec ...' /dev/null (Linux) to capture output.status === "COMPLETED" means "deployment rolled out", not "process exited" — when polling svc.data.status.deployment.status (or top-level status.status in northflank get service -o json), COMPLETED indicates the container is up and serving. The natural reading is the opposite, so don't treat it as a terminal/finished state.| I want to... | Check here |
|---|---|
| Deploy a service from a pre-built image | apiClient.create.service.deployment / northflank create service deployment |
| Build and deploy a service from a Git repo | apiClient.create.service.combined / northflank create service combined; references/guides/build.md |
| Run a one-off command or shell session in a container | apiClient.exec.execServiceCommand / northflank exec service; references/api/execute-command.md |
| Tail or fetch logs from a service, job, or addon | apiClient.get.service.logTail / northflank get service logs -f; references/api/log-tailing.md |
| Forward a private service or addon port to localhost | northflank forward service; references/api/forwarding.md |
| Provision a managed database (Postgres, Redis, Mongo, MySQL…) | apiClient.create.addon / northflank create addon; references/guides/databases-and-persistence.md |
| Wire database credentials into a service via secret groups | apiClient.create.secret with addonDependencies / northflank create secret; references/api/project/secrets/_index.md |
| Create a cron or manual job | apiClient.create.job with settings.cron / `northflank create job cron |
| Configure autoscaling, replicas, or resource sizing | deployment.autoscaling.horizontal field; references/guides/scale.md |
| Pick a compute or GPU plan (sizes, pricing, regions) | references/plans.md — auto-generated from /v1/plans and /v1/regions; slug patterns in Compute & GPU Plans |
| Set up CI/CD: pipelines, release flows, preview environments | references/guides/release.md |
| Define infrastructure as code (templates, GitOps, OpenTofu) | apiClient.create.template / northflank create template; references/guides/infrastructure-as-code.md |
| Add a custom domain with TLS, CDN, or path routing | references/guides/domains.md; references/api/team/domains/_index.md |
| Attach persistent storage / volumes to a service | apiClient.create.volume / northflank create volume; references/api/project/volumes/_index.md |
| Upload or download files into a running container | northflank upload service file / download service file; references/api/copy-files.md |
| Configure ports, network policies, egress IPs, Tailscale | references/guides/network.md |
| Set up log sinks, metrics, alerts, health checks | references/guides/observe.md |
| Run GPU workloads | references/guides/gpu-workloads.md |
| Spin up an AI sandbox / microVM | A sandbox is just a service — use apiClient.create.service.deployment / northflank create service deployment (no sandbox verb exists); references/guides/sandboxes.md |
| Deploy on your own cluster (BYOC/BYOK on AWS/GCP/Azure/CoreWeave) | references/guides/bring-your-own-cloud.md |
| Manage teams, RBAC, SSO/MFA, API tokens | references/guides/secure.md, references/guides/collaborate.md |
| Pause, resume, restart, or delete a resource | apiClient.{pause,resume,restart,delete}.service / northflank {pause,resume,restart,delete} service — re-read Destructive Operations before any delete |
/v1/plans and /v1/regions)npx claudepluginhub northflank/skills --plugin northflankManages Coolify deployments, applications, databases, and services via the Coolify API. Useful for deploying, starting, stopping, restarting, and monitoring applications on Coolify.
Provides quick reference for Fly.io PaaS deployments including fly.toml config, global distribution, scaling patterns, secrets management, health checks, and troubleshooting. Auto-loads on fly.toml detection.
Provides Cloudflare platform knowledge on Workers, storage (R2/D1/KV/Durable Objects/Queues), AI Workers, Hyperdrive, Zero Trust, Workflows, Vectorize, and Wrangler CLI for dev, deployment, and best practices.