Write a template | Infrastructure as Code | Northflank Application docs
v1

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.

You can find more detailed schema for Northflank templates and template nodes in the API documentation for 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.

An example of a project template specification in the Northflank application

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.

An example of a service template specification in the Northflank application

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",
  "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 required

      The version of the Northflank API to run the template against.

      one of
      v1
    • name

      string required

      Name of the template.

    • description

      string

      Description of the template.

    • project

      {object} required

      Contains the ID of an existing project, or describes a new project to be created by the template.

      • concurrencyPolicy

        string

        Defines the concurrency behaviour of the template with respect to parallel runs.

        one of
        forbid, allow, queue
      • 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

            boolean

            If true, the template will run automatically whenever it is updated.

        • spec

          {object} required

          Contains 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 required

              The ID of the project to use.

            OR

          • {object}

            Create a new project

            • spec

              (multiple options: oneOf) required

        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

        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

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Workflow
          • spec

            {object} required

            The 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.

        NodeKindDescription
        Build serviceBuildServiceCreate or update a build service
        Combined serviceCombinedServiceCreate or update a combined service
        Deployment serviceDeploymentServiceCreate or update a deployment service
        Cron jobCronJobCreate or update a cron job
        Manual jobManualJobCreates or update a manual job
        AddonAddonCreates or updates an addon
        Secret groupSecretGroupCreates or updates a secret group
        PipelinePipelineCreates or updates a pipeline
        VolumeVolumeCreates or updates a volume
        BuildBuildTriggers a build in a service or job, from a branch or a specific commit
        Run jobJobRunRuns a job with the specified configuration
        Run backupAddonBackupPerforms 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

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            CombinedService
          • spec

            {object} required

            The specification for the CombinedService node.

        Build service

        • {object}

          BuildService node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            BuildService
          • spec

            {object} required

            The specification for the BuildService node.

        Deployment service

        • {object}

          DeploymentService node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            DeploymentService
          • spec

            {object} required

            The specification for the DeploymentService node.

        Cron job

        • {object}

          CronJob node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            CronJob
          • spec

            {object} required

            The specification for the CronJob node.

        Manual job

        • {object}

          ManualJob node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            ManualJob
          • spec

            {object} required

            The specification for the ManualJob node.

        Addon

        • {object}

          Addon node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Addon
          • spec

            {object} required

            The specification for the Addon node.

        Secret group

        • {object}

          SecretGroup node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            SecretGroup
          • spec

            {object} required

            The specification for the SecretGroup node.

        Pipeline

        • {object}

          Pipeline node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Pipeline
          • spec

            {object} required

            The specification for the Pipeline node.

        Volume

        • {object}

          Volume node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Volume
          • spec

            {object} required

            The specification for the Volume node.

        Build

        • {object}

          BuildService node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            BuildService
          • spec

            {object} required

            The specification for the BuildService node.

        Run job

        • {object}

          JobRun node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            JobRun
          • spec

            {object} required

            The specification for the JobRun node.

          • condition

            string
            one of
            success

        Run addon

        • {object}

          AddonBackup node

          • ref

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            AddonBackup
          • spec

            {object} required

            The specification for the AddonBackup node.

          • condition

            string
            one of
            success

        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.

        NodeKindDescription
        Await conditionConditionContains a condition node that must be met to continue a sequential workflow, or to mark a parallel workflow as successful
        Service conditionServiceContains checks for services
        Addon conditionAddonContains checks for addons
        Backup conditionAddonBackupContains checks for addon backups
        Job run conditionJobRunContains checks for job runs
        Build conditionBuildContains checks for builds
        VCS conditionVCSContains 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

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Condition
          • spec

            (multiple options: oneOf) required

            The 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.

        NodeKindDescription
        ActionActionContains an action node that will perform the specified action on a resource
        Service actionServicePerform an action on a service
        Addon actionAddonPerform an action on an addon
        VCS actionVCSPerform 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

            string

            An identifier that can used to reference the output of this node later in the template.

          • kind

            string required

            The kind of node.

            one of
            Action
          • spec

            (multiple options: oneOf) required

            The specification for the Action node.

        Node references and arguments

        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.

        Example of a sequential template

        This example demonstrates the deployment of a Postgres addon and a WikiJS image from Docker Hub .

        The template goes through the following steps:

        1. Include the details for the template: apiVersion, name, and description
        2. Create a project for the template to run in, specifying the name, region, description, and color. 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.
        3. Add a sequential workflow to include the steps to run in the template:
          1. Create a Postgres addon
          2. Create a secret group with the addon connection details and relevant aliases to provide the database connection details to the application
          3. 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.
        An example of a sequential template run in the Northflank application

        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": []
                    }
                  }
                }
              ]
            }
          }
        }
        

        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:

        1. Include the details for the template: apiVersion, name, and description
        2. Create a project for the template to run in, specifying the name, region, description, and color. 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.
        3. Add a sequential workflow to include the steps to run in the template:
          1. Add a parallel workflow to:
            1. Create a MongoDB addon
            2. Create a deployment service with no instances
            3. Add a sequential workflow to:
              1. Create a build service
              2. Begin a build of the latest commit to the master branch using the build service
          2. After the parallel workflow has executed a sequential workflow is added:
            1. The condition node checks the MongoDB addon has initialised and is running
            2. A secret group is created containing the connection details for the addon and the DNS entry for the deployment service
            3. The condition node checks the build has completed
            4. The deployment service is updated to deploy the master branch from the build service, and scaled to 1 instance, now inheriting the necessary secrets
        A parallel template run in the Northflank application
        {
          "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"
                          }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
        

        © 2023 Northflank Ltd. All rights reserved.