Loading...
Loading...
Deploy, manage, and automate infrastructure on Northflank — a developer platform for building, deploying, and scaling services, jobs, databases, and release workflows on Kubernetes. Use when managing Northflank projects, services, jobs, addons (databases), secrets, domains, templates (IaC), environments, preview environments, pipelines, release flows, BYOC/BYOK clusters, GPU workloads, AI sandboxes, or interacting with the Northflank API/CLI/JS client. Covers REST API, CLI (`northflank` command), and JavaScript client (`@northflank/js-client`).
npx skill4agent add northflank/skills northflankNote: CLI examples in this file (and the other hand-maintained references) are checked against, 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 <verb> <noun> --help
https://api.northflank.com/v1/Authorization: Bearer <token>x-ratelimit-remainingnorthflank login@northflank/js-clientNF_API_TOKENnorthflank --helpnpm install -g @northflank/cli
# or
yarn global add @northflank/clipackage.json@northflank/js-clientnpm install @northflank/js-client
# or
yarn add @northflank/js-clientNF_API_TOKENprojectId"My Project"my-projectnorthflank context use projectapiClient.list.projects({})delete serviceapiClient.delete.servicedelete addonapiClient.delete.addon<addon-id>delete volumedelete projectdelete secretdelete jobdelete templatedelete domaindelete dns-recorddelete clusterpatchupdatepause servicedelete servicenpm i -g @northflank/cli
northflank login
northflank context ls
northflank context use
northflank context use projectnorthflank loginnorthflank context use project|service|jobnorthflank command-overview# Prompts for missing values interactively
northflank get service
# Explicit project and service
northflank get service --projectId <PROJECT_ID> --serviceId <SERVICE_ID># 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 idnorthflank exec jobsudo 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>'--skipHostnamesnorthflank forward all --projectId <PROJECT_ID>northflank create project --help
northflank create project --file ./project.yamlimport { 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);apiClient.{verb}.{resource}({ parameters, data, options })parametersprojectIdserviceIddataoptionsconst 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'nf-compute-10nf-compute-50nf-compute-100-2nf-compute-200await 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();references/api/execute-command.mdconst 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 doneconst 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);// 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' } },
});// 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'); // secondsscripts/generate_references.js--forcecurl -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))}'apiClient.list.plans({})apiClient.list.regions({})northflank list plansnorthflank list regionsnf-compute-<cpu*100>-<ram_gb>nf-compute-<cpu*100>nf-compute-10nf-compute-50nf-compute-200nf-compute-400-16buildPlanbuildPlannf-compute-400-16nf-gpu-<gpuType>-<count>ggnf-compute-*data: {
billing: { deploymentPlan: 'nf-gpu-a100-80-1g' }, // 1× A100 80GB
deployment: {
gpu: { enabled: true, gpuType: 'a100-80', gpuCount: 1 },
// ...
},
}gpuTypenvidia-l4-24a100-40a100-80h100-80h200-141b200-180gpuCountcountOptions1, 2, 4, 8gpudeployment.gpubilling.gpudeployment.gpuapiClient.list.cloudProviders.nodeTypes({ options: { hasGpu: true } })references/guides/bring-your-own-cloud.md#create-custom-resource-planseurope-docker.pkg.dev/northflank/public/...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"my-apix-ratelimit-remainingput.*northflank exec --cmd "..."bash -cscript -q /dev/null northflank exec ...script -qfc 'northflank exec ...' /dev/nullstatus === "COMPLETED"svc.data.status.deployment.statusstatus.statusnorthflank get service -o jsonCOMPLETED| I want to... | Check here |
|---|---|
| Deploy a service from a pre-built image | |
| Build and deploy a service from a Git repo | |
| Run a one-off command or shell session in a container | |
| Tail or fetch logs from a service, job, or addon | |
| Forward a private service or addon port to localhost | |
| Provision a managed database (Postgres, Redis, Mongo, MySQL…) | |
| Wire database credentials into a service via secret groups | |
| Create a cron or manual job | |
| Configure autoscaling, replicas, or resource sizing | |
| Pick a compute or GPU plan (sizes, pricing, regions) | references/plans.md — auto-generated from |
| Set up CI/CD: pipelines, release flows, preview environments | |
| Define infrastructure as code (templates, GitOps, OpenTofu) | |
| Add a custom domain with TLS, CDN, or path routing | |
| Attach persistent storage / volumes to a service | |
| Upload or download files into a running container | |
| Configure ports, network policies, egress IPs, Tailscale | |
| Set up log sinks, metrics, alerts, health checks | |
| Run GPU workloads | |
| Spin up an AI sandbox / microVM | A sandbox is just a service — use |
| Deploy on your own cluster (BYOC/BYOK on AWS/GCP/Azure/CoreWeave) | |
| Manage teams, RBAC, SSO/MFA, API tokens | |
| Pause, resume, restart, or delete a resource | |
/v1/plans/v1/regions