Infrastructure as Code /
Write a template
Templates are JSON definitions that consist of different types of nodes which specify workflows, resources, actions or conditions. When you create a template you can either edit the template JSON as a whole, or use the visual editor to drag and drop nodes.
You can edit individual nodes in the visual editor using the form view or switch to code view, which is especially useful to paste in resource specifications.
You can create simple templates from your project specifications in order to duplicate or update your existing resources. You can combine this with GitOps to manage your resources as code, for example your can scale up a deployment or change network configuration by pushing an updated template to your repository.
However, you can create templates that are more easily managed and updated by replacing hard-coded values with references and arguments, which can dynamically populate the template when it is run. This becomes especially useful if you are working with a large, complex template, or want to share your template so others can use and modify it. You can also use functions to return different values based on arguments or other variables, for example assigning a different domain to a port depending on the branch name.
This guide will first introduce best practices for writing templates, and how to get automatically generated template specifications from your existing resources and projects. It will then run through the structure of a template and how to use node references and arguments, which are important to creating programmatic templates that are easy to modify and reuse. The different types of nodes and their properties will then be detailed, 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.
Template specifications
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.
Template structure
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",
"arguments": {},
"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, queuearguments
{object}A set of arguments that can be referenced in a template using '${args.argumentName}'.
argumentOverrides
{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.
Template project
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.
Types of node
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
Arguments and argument overrides
You can include arguments in your template, referenced in the format ${args.<argument-name>}
, replacing <argument-name>
with your key. This is useful if you have a value you wish to dynamically generate, change on subsequent template runs, or which is used repeatedly throughout the template.
Arguments are stored in the argument object at the top-level of the template as key-value pairs. You can also set argument values in the UI using the template form's arguments override section, however these will not be saved in the template specification.
Argument overrides
Argument overrides can be used to inject secure values into your template, or override existing argument values. Key-value pairs in the argumentOverrides
object will override arguments with the same key in the arguments
object.
Argument overrides are stored securely on Northflank, separately from your template. If you are using GitOps these will not be saved in your repository if you add the argument overrides in the Northflank UI using the template form. You should not commit templates that include argument overrides.
You can supply argument overrides using the API by including an argumentOverride
object at the top-level of the template, containing the key-value pairs of arguments to override.
Run on creation overrides
If you are creating a template using the API you can also specify runOnCreationArgumentOverrides
in options
, which will only be used when the template is created if runOnCreation
is set to true
.
Example
{
"name": "Example Template",
"description": "This is a sample template.",
"apiVersion": "v1",
"arguments": {
"ARGUMENT_1": "default_1",
"ARGUMENT_2": "default_2",
"ARGUMENT_3": ""
},
"argumentOverrides": {
"ARGUMENT_1": "hello",
"ARGUMENT_2": "world",
"ARGUMENT_3": ""
},
"spec": {
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
...
]
}
},
"options": {
"runOnCreation": true,
"runOnCreationArgumentOverrides": {
"ARGUMENT_1": "value_1",
"ARGUMENT_2": "value_2",
"ARGUMENT_3": "value_3"
},
"autorun": false
}
}
Node references
You can add a reference property to nodes which allows you to refer to the output of that node later in the template.
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.
You can see how references are used in the parallel template example.
Northflank DNS references
You can obtain the public DNS for a service from a reference in the following format: ${refs.<service-name>.ports.<array-number>.dns}
. Ports are an array of objects, you must provide the array position of the port you want to get the DNS for. For example if the first port on your service is a public HTTP port, you would obtain the DNS with the following: ${refs.<service-name>.ports.0.dns}
.
If you are using references to obtain the Northflank-generated DNS for a service, or connection details for an addon, you should include a condition node to make sure the service or addon is running. This will ensure the service or addon has obtained a subdomain before using the reference to obtain the DNS or connection details.
Dynamic domains
You can dynamically create subdomains in templates by configuring your domain to use wildcard redirect routing and certificate generation when you add it to Northflank.
You can then use the values from references and arguments to assign subdomains to your services, for example "${args.<argument-name>}.example.com"
or "${refs.<reference-name>.<property>}.example.com"
.
You can also accept requests to any subdomain of the parent domain using wildcard subdomains.
Functions
You can include functions in your template. Functions are called in the format: "${fn.<function-name>(<arguments>)}"
, for example "${fn.randomSecret(32, 'hex')}"
.
Functions are deterministic and will be evaluated on every template run, excluding the randomSecret
function.
Functions can include references and arguments that do not resolve to a value, unlike the top-level of a template where references and arguments must resolve. Unresolved references and arguments will be treated as false
if used as boolean arguments.
Functions are listed below, consisting of the function name, arguments and their types, and the purpose of the function.
General
Function | Arguments | Description |
---|---|---|
randomSecret | length: number , encoding: string: 'base64' or 'hex' | Returns a random base64 secret of the given length , and an optional encoding argument, either 'base64' (default) or 'hex'. This secret will be securely stored in the target resource and remain unchanged during subsequent executions of the template, unless it is manually removed. |
String manipulation
Function | Arguments | Description |
---|---|---|
toBase64 | string: string | Converts a UTF-8 encoded string to a base64-encoded string |
fromBase64 | base64: base64 | Converts a base64-encoded string to a UTF-8 encoded string |
slug | string: string | Converts a string to a slug (lowercase string with hyphens instead of spaces) |
indexOf | string: string , match: string | Returns the index of the first instance of the substring in the string, or -1 if not found |
search | string: string , match: string or regex | Returns the index of the first pattern match in the string, or -1 for no match |
replace | original: string , match: string or regex , replacement: string | Replace the first match in the original string with the replacement string |
replaceAll | original: string , match: string or regex , replacement: string | Replace all instances of the match in the original string with the replacement string |
slice | original: string , startIndex: integer , endIndex: integer | Returns the string between the indices of the original string |
length | string: string | Returns the length of the string as an integer |
Boolean functions
Boolean arguments can be provided as truthy and falsy values similar to JavaScript. They can accept booleans, strings, and numbers, and if a reference or argument does not resolve, it will be regarded as false.
Function | Arguments | Description |
---|---|---|
not | boolean: boolean | Not |
or | boolean1: boolean , boolean2: boolean , ... | Or, accepts any number of arguments |
and | boolean1: boolean , boolean2: boolean , ... | And, accepts any number of arguments |
if | boolean: boolean , then: any , else: any (optional) | If, returns then argument if true, otherwise returns else argument if provided |
eq | equal1: any , equal2: any , ... | Equals, accepts any number of arguments |
neq | not1: any , not2: any , ... | Not equals, accepts any number of arguments |
gt | num1: number , num2: number | Greater than |
lt | num1: number , num2: number | Lesser than |
gte | num1: number , num2: number | Greater than or equal to |
lte | num1: number , num2: number | Lesser than or equal to |
Maths
Function | Arguments | Description |
---|---|---|
add | a: number , b: number , ... | Add all arguments, accepts any number of arguments |
subtract | a: number , b: number , ... | Subtract all arguments, accepts any number of arguments |
multiply | a: number , b: number , ... | Multiply all arguments, accepts any number of arguments |
divide | a: number , b: number | Divide a by b |
remainder | a: number , b: number | The remainder of a divided by b |
exp | a: number , b: number | a to the power of b |
floor | a: number | The floor of a |
ceil | a: number | The ceiling of a |
Example
This example template uses references, arguments, and functions to programmatically build and deploy from a Git repository.
The Git account, repository, and branch are given as arguments to define the service names and retrieve the Git account and repository URL. References are then used to trigger a build and deploy it in the deployment service.
The resources assigned to the deployment service depend on the name of the branch, combining if
and eq
functions, as well as passing different runtime variables to the deployment.
{
"apiVersion": "v1",
"spec": {
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "BuildService",
"ref": "builder",
"spec": {
"name": "${args.repository}-builder",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"vcsData": {
"projectUrl": "https://github.com/${args.account}/${args.repository}",
"projectType": "github"
},
"buildSettings": {
"dockerfile": {
"buildEngine": "kaniko",
"dockerFilePath": "/Dockerfile",
"dockerWorkDir": "/"
}
},
"buildConfiguration": {
"prRestrictions": [
"*"
],
"branchRestrictions": [
"main"
]
}
}
},
{
"kind": "Build",
"ref": "build",
"spec": {
"id": "${refs.builder.id}",
"type": "service",
"branch": "${args.branch}"
}
},
{
"kind": "Condition",
"spec": {
"kind": "Build",
"spec": {
"type": "success",
"data": {
"buildId": "${refs.build.id}"
}
}
}
},
{
"kind": "DeploymentService",
"spec": {
"name": "${args.branch}-deployment",
"billing": {
"deploymentPlan": "${fn.if(fn.eq(args.branch, 'main'), 'nf-compute-100', 'nf-compute-50')}"
},
"deployment": {
"instances": "${fn.if(fn.eq(args.branch, 'main'), 3, 1)}",
"docker": {
"configType": "default"
},
"storage": {
"ephemeralStorage": {
"storageSize": 1024
}
},
"internal": {
"id": "${refs.builder.id}",
"branch": "${args.branch}",
"buildId": "${refs.build.id}"
}
},
"runtimeEnvironment": {
"ENVIRONMENT": "${fn.if(fn.eq(args.branch, 'main'), 'production', 'development')}"
}
}
}
]
}
}
}
Workflow nodes
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
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}
Build 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 ofBuildspec
{object} requiredThe specification for the Build node.
condition
stringone ofsuccess
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 backup
- {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
Condition nodes
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.
Action nodes
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"
}
}
}
}
Execute a command in a service
{
"kind": "Action",
"spec": {
"kind": "Service",
"spec": {
"type": "execute",
"data": {
"serviceId": "my-service",
"command": "sh -c \"echo ${MESSAGE}\""
}
}
}
}
Commands in action nodes do not invoke a shell by default. Learn more about executing commands in action nodes.
- {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.
Example of a sequential template
This example demonstrates the deployment of a PostgreSQL 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 PostgreSQL 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": "Sequential Payload template",
"description": "Example of a template with a sequential workflow",
"project": {
"spec": {
"name": "Sequential Payload template",
"region": "europe-west",
"color": "#7FD1B9",
"description": "Project created by an example template with a sequential workflow"
}
},
"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
}
},
{
"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",
"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
}
}
}
},
{
"kind": "SecretGroup",
"spec": {
"name": "secrets",
"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"
]
}
]
}
]
}
}
]
}
}
}
Example of a parallel template
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 the template continues running sequentially:
- A secret group is created containing the connection details for the addon, the DNS entry for the deployment service, and a random secret
- A parallel workflow is added with:
- A condition node to check the MongoDB addon has initialised and is running
- A condition node to check the build has completed
- After both checks are successful 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:
{
"name": "Parallel Payload template",
"description": "Example of a template with parallel workflows",
"project": {
"spec": {
"name": "Parallel Payload template",
"region": "europe-west",
"color": "#6F2DBD",
"description": "Project created by an example template with parallel workflows"
}
},
"apiVersion": "v1",
"spec": {
"kind": "Workflow",
"spec": {
"type": "sequential",
"steps": [
{
"kind": "Workflow",
"spec": {
"type": "parallel",
"steps": [
{
"kind": "Addon",
"ref": "payloadDatabase",
"spec": {
"billing": {
"replicas": 1,
"storage": 4096,
"deploymentPlan": "nf-compute-50",
"storageClass": "ssd"
},
"name": "Payload database",
"type": "mongodb",
"version": "7-latest",
"tlsEnabled": true,
"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"
]
},
"disabledCI": false,
"buildArguments": {},
"buildFiles": {}
},
"ref": "payloadBuilder"
},
{
"kind": "Build",
"ref": "payloadBuild",
"spec": {
"id": "${refs.payloadBuilder.id}",
"type": "service",
"branch": "master",
"buildOverrides": {
"buildArguments": {}
}
}
}
]
}
},
{
"kind": "DeploymentService",
"ref": "payloadDeploy",
"spec": {
"deployment": {
"instances": 0,
"storage": {
"ephemeralStorage": {
"storageSize": 1024
}
},
"docker": {
"configType": "default"
}
},
"name": "Payload deployment",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"ports": [
{
"name": "p01",
"internalPort": 3000,
"public": true,
"protocol": "HTTP",
"security": {
"credentials": [],
"policies": []
},
"domains": [],
"disableNfDomain": false
}
],
"runtimeEnvironment": {},
"runtimeFiles": {}
}
}
]
}
},
{
"kind": "SecretGroup",
"spec": {
"priority": 10,
"name": "payload-secrets",
"secretType": "environment-arguments",
"addonDependencies": [
{
"addonId": "${refs.payloadDatabase.id}",
"keys": [
{
"keyName": "MONGO_SRV",
"aliases": [
"MONGODB_URI"
]
}
]
}
],
"secrets": {
"variables": {
"PAYLOAD_SECRET": "${fn.randomSecret(32)}",
"PAYLOAD_PUBLIC_BASE_DNS": "https://${refs.payloadDeploy.ports.0.dns}"
}
},
"restrictions": {
"restricted": false,
"nfObjects": [],
"tags": []
}
}
},
{
"kind": "Workflow",
"spec": {
"type": "parallel",
"steps": [
{
"kind": "Condition",
"spec": {
"kind": "Build",
"spec": {
"type": "success",
"data": {
"buildId": "${refs.payloadBuild.id}"
}
}
}
},
{
"kind": "Condition",
"spec": {
"kind": "Addon",
"spec": {
"type": "running",
"data": {
"addonId": "${refs.payloadDatabase.id}"
}
}
}
}
]
}
},
{
"kind": "DeploymentService",
"spec": {
"deployment": {
"instances": 1,
"internal": {
"buildId": "${refs.payloadBuild.id}"
},
"docker": {
"configType": "default"
}
},
"name": "Payload deployment",
"billing": {
"deploymentPlan": "nf-compute-50"
},
"ports": [],
"runtimeEnvironment": {},
"runtimeFiles": {}
}
}
]
}
}
}