Infrastructure as Code /
Write a template
Templates are written as nested JSON that consist of different types of node. When you create a template you can either edit the template JSON as a whole, or use the visual editor and edit the code of individual nodes.
This guide will first introduce how to get automatically generated template specifications from your existing resources and projects. It will then run through the structure of a template and different types of nodes before providing some examples of templates.
Properties not mentioned in the application documentation are only applicable to the Northflank API, and should not be included in templates managed using the Northflank application or a Git repository.
The simplest way to start writing a template is to get the template specification for your existing resources or an entire project.
You can also create new projects or resources in order to copy the specification for use in a template, rather than writing the specification from scratch.
View project template
You can click the options button in the project header to get the template for an entire project.
Select view project template to see the specification for your entire project. You can copy this and use the content to create a new template, and edit it where required.

View resource specification
To view the specification for individual resources, such as services, addons, jobs you can click the options button in the resource's header.
Select view specification and ensure the view is toggled to template. You can copy the specification and insert it into your template.

Your template must include information about the template itself, such as apiVersion
and name
(and optionally a description
). You must also include a project
object, which defines the project to create or update, or an existing project ID to run the template in, and the spec
object, which contains the workflows, conditions, resources, and actions of the template. You can also include concurrencyPolicy
, argumentOverrides
, and options
objects
If you create or edit a template via the Northflank application all fields except apiVersion
and spec
are configured in the form, and are hidden from the JSON view. The Northflank editor also includes code hints, autocompletion, and error checking to help you include required properties in the correct format.
The top level of a template looks like this:
{
"apiVersion": "v1",
"name": "template-name",
"description": "Your template description",
"project": {},
"concurrencyPolicy": "allow",
"argumentOverrides": {},
"options": {
"autorun": false
},
"spec": {}
}
If you are writing a template as a JSON file the required fields, listed in the attributes below, must be included. You can also see the examples further down on this page of complete templates.
- {object}
apiVersion
string requiredThe version of the Northflank API to run the template against.
one ofv1name
string requiredName of the template.
description
stringDescription of the template.
project
{object} requiredContains the ID of an existing project, or describes a new project to be created by the template.
concurrencyPolicy
stringDefines the concurrency behaviour of the template with respect to parallel runs.
one offorbid, allow, queueargumentOverrides
{object}Argument overrides stored outside of the template. If GitOps is enabled, these will not be saved in version control.
options
{object}Additional options for the template creation.
autorun
booleanIf true, the template will run automatically whenever it is updated.
spec
{object} requiredContains nodes representing actions to be performed as part of the template.
You can provide either the ID of an existing project to run the template in, or specify a project to be created or updated. You cannot update the region or cluster of a project.
A new project specification looks like this:
{
"project": {
"spec": {
"name": "My new project",
"description": "A new project created by this template",
"color": "#57637A",
"region": "europe-west"
}
}
}
If you are using another cloud provider, you can provide clusterId
instead of region
, where clusterId
is the ID of the cluster you want to use.
To use an existing project you can provide only the project ID:
{
"id": "your-project-id"
}
The project object can contain one of the following:
- (multiple options: oneOf)
Details of the project the template will run in.
- {object}
Use an existing project
id
string requiredThe ID of the project to use.
- {object}
Create a new project
spec
(multiple options: oneOf) required
OR
If you run a template that creates a project with the same name as an existing project in the account, it will update the project according to the template. If the template specifies a region different to the existing project, the template run will fail.
If you run a template that specifies (but does not create) a project that doesn't exist in the account, the template run will fail.
The sections below contain information on the available node types that you can include in your template. Each section contains a list of nodes of that type, an example node specification, and references from the API.
The nodes are divided into the following categories:
- Workflow: contains nodes that are to be run in sequence or parallel
- Resource: specifies a project, service, job, addon, or other resource to be created or updated
- Trigger: initiates a task within a service, job, or addon
- Condition: includes a condition node that checks the status of a resource or action
- Action: contains an action node that specifies an action to run on a service, repository, or other entity
Workflow nodes specify whether the nodes contained within them are executed sequentially, or in parallel.
You can add nodes to the steps
array which will be executed in order in a sequential workflow, or at the same time for a parallel workflow.
Workflow nodes can be nested. You can, for example, run two sequential workflows simultaneously within a parallel workflow.
Workflow nodes take the following format, where nodes would be specified in the steps
array:
{
"kind": "workflow",
"spec": {
"type": "sequential | parallel",
"steps": []
}
}
- {object}
Workflow node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofWorkflowspec
{object} requiredThe specification for the workflow node.
Resource nodes can be used to create or update services, jobs, addons, and other resources on the Northflank platform. They can also be used to trigger builds, run jobs, and schedule addon backups.
Node | Kind | Description |
---|---|---|
Build service | BuildService | Create or update a build service |
Combined service | CombinedService | Create or update a combined service |
Deployment service | DeploymentService | Create or update a deployment service |
Cron job | CronJob | Create or update a cron job |
Manual job | ManualJob | Creates or update a manual job |
Addon | Addon | Creates or updates an addon |
Secret group | SecretGroup | Creates or updates a secret group |
Pipeline | Pipeline | Creates or updates a pipeline |
Volume | Volume | Creates or updates a volume |
Build | Build | Triggers a build in a service or job, from a branch or a specific commit |
Run job | JobRun | Runs a job with the specified configuration |
Run backup | AddonBackup | Performs a backup on an addon |
The below example creates a deployment service called nginx
, deploying the nginx image from Docker Hub . It also publicly exposes port 80 using HTTP.
{
"kind": "DeploymentService",
"spec": {
"name": "nginx",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"deployment": {
"instances": 1,
"external": {
"imagePath": "library/nginx:latest"
}
},
"ports": [
{
"name": "port-01",
"internalPort": 80,
"public": true,
"protocol": "HTTP"
}
]
}
}
Combined service
- {object}
CombinedService node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofCombinedServicespec
{object} requiredThe specification for the CombinedService node.
Build service
- {object}
BuildService node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofBuildServicespec
{object} requiredThe specification for the BuildService node.
Deployment service
- {object}
DeploymentService node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofDeploymentServicespec
{object} requiredThe specification for the DeploymentService node.
Cron job
- {object}
CronJob node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofCronJobspec
{object} requiredThe specification for the CronJob node.
Manual job
- {object}
ManualJob node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofManualJobspec
{object} requiredThe specification for the ManualJob node.
Addon
- {object}
Addon node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofAddonspec
{object} requiredThe specification for the Addon node.
Secret group
- {object}
SecretGroup node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofSecretGroupspec
{object} requiredThe specification for the SecretGroup node.
Pipeline
- {object}
Pipeline node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofPipelinespec
{object} requiredThe specification for the Pipeline node.
Volume
- {object}
Volume node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofVolumespec
{object} requiredThe specification for the Volume node.
Build
- {object}
BuildService node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofBuildServicespec
{object} requiredThe specification for the BuildService node.
Run job
- {object}
JobRun node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofJobRunspec
{object} requiredThe specification for the JobRun node.
condition
stringone ofsuccess
Run addon
- {object}
AddonBackup node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofAddonBackupspec
{object} requiredThe specification for the AddonBackup node.
condition
stringone ofsuccess
You can use condition nodes to check the status of a resource or action in a template or in an individual workflow. The workflow or template will continue to run until the condition is met, or until it times out.
In a sequential workflow the condition will stop following steps from running until the condition is met. In a parallel workflow all the steps will run, but the workflow will not be marked as completed unless the condition is met.
Below is a list of available condition node types you can include in your template. Specific condition nodes must be contained within the parent Condition
node.
Node | Kind | Description |
---|---|---|
Await condition | Condition | Contains a condition node that must be met to continue a sequential workflow, or to mark a parallel workflow as successful |
Service condition | Service | Contains checks for services |
Addon condition | Addon | Contains checks for addons |
Backup condition | AddonBackup | Contains checks for addon backups |
Job run condition | JobRun | Contains checks for job runs |
Build condition | Build | Contains checks for builds |
VCS condition | VCS | Contains checks for Git repositories |
An example of a condition node specification that checks if the referenced build has completed successfully:
{
"kind": "Condition",
"spec": {
"kind": "Build",
"spec": {
"type": "success",
"data": {
"buildId": "${refs.build.id}"
}
}
}
}
- {object}
Condition node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofConditionspec
(multiple options: oneOf) requiredThe specification for the Condition node.
You can define actions to take on existing services, addons, or Git services using action nodes.
You can, for example, restart a service or addon, or clone an existing repository into a new one. This can be useful if you have deployed a service that requires restarting after initialising, or want to share a template and enable users to modify the source code.
Below is a list of available action node types you can include in your template. Specific action nodes must be contained within the parent Action
node.
Node | Kind | Description |
---|---|---|
Action | Action | Contains an action node that will perform the specified action on a resource |
Service action | Service | Perform an action on a service |
Addon action | Addon | Perform an action on an addon |
VCS action | VCS | Perform an action on a Git account |
The example below triggers a restart of a service:
{
"kind": "Action",
"spec": {
"kind": "Service",
"spec": {
"type": "restart",
"data": {
"serviceId": "service-to-restart"
}
}
}
}
- {object}
Action node
ref
stringAn identifier that can used to reference the output of this node later in the template.
kind
string requiredThe kind of node.
one ofActionspec
(multiple options: oneOf) requiredThe specification for the Action node.
You can add a reference (ref
) property to nodes to refer to the output of a node. Add your ref
as a string and it will return a promise, which resolves to the relevant response. References are accessed in the template using the refs
object, in the format ${refs.<reference-name>.property}
.
Check the response schemas in the API documentation.
This is useful if you have steps in your workflow that require confirmation that previous steps have been completed successfully, or details from resources created earlier.
For example, if you are deploying an app that requires a database you could use the reference to the database creation node to ensure that the database has been created and get the connection details to pass to your deployment.
You can see this implemented in some of the example templates.
Arguments
You can include arguments in your template, referenced in the format ${args.argumentName}
, replacing argumentName
with the key saved in argument overrides. You can then set their values in the template form arguments override section, by creating key-value pairs with the argument name: "argName": "value"
.
Argument overrides are stored outside the template. If you are using GitOps, these will not be saved in your repository.
To manage arguments using the API you can create arguments
and argumentOverride
objects, containing the key-value pairs.
Functions
You can include functions in your template. If you use a function in a template it will be saved as a function call. The function will be evaluated every time you run the template, which means the value can change if a template is used to update resources.
This example demonstrates the deployment of a Postgres addon and a WikiJS image from Docker Hub .
The template goes through the following steps:
- Include the details for the template:
apiVersion
,name
, anddescription
- Create a project for the template to run in, specifying the
name
,region
,description
, andcolor
. If a project with this name exists in the region, it will update the resources in the project as given in the template when run. If a project with this name exists in a different region, the template run will fail. - Add a sequential workflow to include the steps to run in the template:
- Create a Postgres addon
- Create a secret group with the addon connection details and relevant aliases to provide the database connection details to the application
- Create a deployment service, pulling the external image from Docker Hub. It has two ports configured, a public HTTP port and a private TCP port to connect to the database.

When the template runs, it will wait for the previous step in the workflow to complete before executing the next. The database is created first in order to create a secret group with the addon connection details. As the secret group is unrestricted, the deployment created afterwards will then inherit the connection detail secrets with the necessary aliases for the application.
{
"apiVersion": "v1",
"name": "Payload template",
"description": "",
"project": {
"spec": {
"name": "My project",
"region": "europe-west",
"color": "#7FD1B9",
"description": ""
}
},
"concurrencyPolicy": "allow",
"argumentOverrides": {},
"options": {
"autorun": false
},
"spec": {
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "Addon",
"spec": {
"name": "database",
"type": "mongodb",
"version": "latest",
"billing": {
"deploymentPlan": "nf-compute-50",
"storageClass": "ssd",
"storage": 4096,
"replicas": 3
},
"tlsEnabled": true,
"externalAccessEnabled": false,
"ipPolicies": [],
"pitrEnabled": false
}
},
{
"kind": "CombinedService",
"ref": "payload",
"spec": {
"name": "payload",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"deployment": {
"instances": 3,
"storage": {
"ephemeralStorage": {
"storageSize": 1024
}
}
},
"ports": [
{
"name": "p01",
"internalPort": 3000,
"public": true,
"protocol": "HTTP",
"security": {
"credentials": [],
"policies": []
},
"domains": [],
"disableNfDomain": false
}
],
"vcsData": {
"projectUrl": "https://github.com/northflank-guides/deploy-payload-on-northflank",
"projectType": "github",
"projectBranch": "master"
},
"buildSettings": {
"dockerfile": {
"buildEngine": "kaniko",
"dockerFilePath": "/Dockerfile",
"dockerWorkDir": "/",
"useCache": false
}
},
"healthChecks": [],
"description": "",
"buildConfiguration": {
"pathIgnoreRules": []
},
"runtimeEnvironment": {},
"buildArguments": {},
"disabledCI": false
}
},
{
"kind": "SecretGroup",
"spec": {
"name": "secrets",
"description": "",
"secretType": "environment-arguments",
"priority": 10,
"secrets": {
"variables": {
"PAYLOAD_PUBLIC_BASE_DNS": "https://${refs.payload.ports.0.dns}",
"PAYLOAD_SECRET": "${fn.randomSecret(32)}"
}
},
"addonDependencies": [
{
"addonId": "database",
"keys": [
{
"keyName": "MONGO_SRV",
"aliases": [
"MONGODB_URI"
]
}
]
}
],
"restrictions": {
"restricted": false,
"nfObjects": []
}
}
}
]
}
}
}
This example demonstrates the deployment of a MongoDB addon and the build and deployment of a Payload server .
The project resources can be initialised simultaneously, so the services and addon are created at the same time. Conditions are then used to check that the database is running and that the build has completed, before creating a secret group and deploying the build. This means that the resources can be created faster, and steps are only executed when they should succeed.
The template goes through the following steps:
- Include the details for the template:
apiVersion
,name
, anddescription
- Create a project for the template to run in, specifying the
name
,region
,description
, andcolor
. If a project with this name exists in the region, it will update the resources in the project as given in the template when run. If a project with this name exists in a different region, the template run will fail. - Add a sequential workflow to include the steps to run in the template:
- Add a parallel workflow to:
- Create a MongoDB addon
- Create a deployment service with no instances
- Add a sequential workflow to:
- Create a build service
- Begin a build of the latest commit to the master branch using the build service
- After the parallel workflow has executed a sequential workflow is added:
- The condition node checks the MongoDB addon has initialised and is running
- A secret group is created containing the connection details for the addon and the DNS entry for the deployment service
- The condition node checks the build has completed
- The deployment service is updated to deploy the master branch from the build service, and scaled to 1 instance, now inheriting the necessary secrets
- Add a parallel workflow to:

{
"apiVersion": "v1",
"name": "Parallel template",
"description": "",
"project": {
"spec": {
"name": "Parallel template",
"region": "europe-west",
"color": "#6F2DBD",
"description": ""
}
},
"concurrencyPolicy": "allow",
"argumentOverrides": {},
"options": {
"autorun": false
},
"spec": {
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "Workflow",
"spec": {
"type": "parallel",
"steps": [
{
"kind": "Addon",
"spec": {
"name": "payload-db",
"description": "",
"type": "mongodb",
"version": "latest",
"billing": {
"deploymentPlan": "nf-compute-50",
"storageClass": "ssd",
"storage": 4096,
"replicas": 1
},
"tlsEnabled": false,
"externalAccessEnabled": false,
"ipPolicies": [],
"pitrEnabled": false
}
},
{
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "BuildService",
"spec": {
"name": "payload-builder",
"billing": {
"deploymentPlan": "nf-compute-100-1"
},
"vcsData": {
"projectUrl": "https://github.com/northflank-guides/deploy-payload-on-northflank",
"projectType": "github"
},
"buildSettings": {
"dockerfile": {
"buildEngine": "kaniko",
"dockerFilePath": "/Dockerfile",
"dockerWorkDir": "/"
}
},
"buildConfiguration": {
"prRestrictions": ["*"],
"branchRestrictions": ["master"]
}
}
},
{
"kind": "Build",
"ref": "payloadBuild",
"spec": {
"id": "payload-builder",
"type": "service",
"branch": "master"
}
}
]
}
},
{
"kind": "DeploymentService",
"ref": "payloadDeploy",
"spec": {
"name": "payload-deployment",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"deployment": {
"instances": 0,
"storage": {
"ephemeralStorage": {
"storageSize": 1024
}
}
},
"ports": [
{
"name": "p01",
"internalPort": 3000,
"public": true,
"protocol": "HTTP"
}
],
"healthChecks": []
}
}
]
}
},
{
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "Condition",
"spec": {
"kind": "Addon",
"spec": {
"type": "running",
"data": {
"addonId": "payload-db"
}
}
}
},
{
"kind": "SecretGroup",
"spec": {
"name": "payload-secrets",
"description": "",
"secretType": "environment-arguments",
"priority": 10,
"addonDependencies": [
{
"addonId": "payload-db",
"keys": [
{
"keyName": "MONGO_SRV",
"aliases": ["MONGODB_URI"]
}
]
}
],
"secrets": {
"variables": {
"PAYLOAD_SECRET": "${fn.randomSecret(32)}",
"PAYLOAD_PUBLIC_BASE_DNS": "https://${refs.payloadDeploy.ports.0.dns}"
}
}
}
},
{
"kind": "Condition",
"spec": {
"kind": "Build",
"spec": {
"type": "success",
"data": {
"buildId": "${refs.payloadBuild.id}"
}
}
}
},
{
"kind": "DeploymentService",
"spec": {
"name": "payload-deployment",
"deployment": {
"instances": 1,
"internal": {
"id": "payload-builder",
"branch": "master"
}
},
"billing": {
"deploymentPlan": "nf-compute-50"
}
}
}
]
}
}
]
}
}
}