Create Sample App
We'll show you how to spin up and configure an application in minutes.

Introduction to Application Deployments

Welcome to the first of our getting started guides. In this guide, you'll learn the basics of Dynamic Configuration Management.
When deploying an Application (App), your deployment might typically depend on:
  • A Kubernetes Cluster
  • A Container Image
  • A Database Service
  • A DNS Service
While creating an App from scratch is not something you will do day-to-day, it provides a helpful backdrop to explore Humanitec.
To keep things simple new accounts come pre-populated with some basic infrastructure and sample images provided by Humanitec.
For the duration of this guide, we'll assume that you're using such an account. However, an account under an existing organization would likely look somewhat similar.
If you don't have a Humanitec account, head to our website to request a free trial. We will send you instructions on starting your free trial immediately.
When you're ready to connect your infrastructure, follow the Connect your own infrastructure guide, where we cover subjects like adding your K8S clusters, Database & DNS services, and much more.

What is an App in Humanitec

An App represents one or more Workloads that will run in a single Kubernetes namespace. Humanitec enables users to define the environment agnostic configuration of these Apps via Sets (The configuration at a point in time) and Deltas (A change/diff that can be applied to a Set). Conceptually, this is similar to Git, where a commit is essentially a diff that can be applied to the code base.
Using environment agnostic configurations means you can define the App once and then deploy it on any environment (public cloud, private cloud, on-premise, etc.), provided you've got suitable resource definitions in your Humanitec account. Preventing configuration drift between Development, QA, and Production environments - As Humanitec handles the generation of the relevant manifests and provisioning of resources for you. We cover this in more detail in the Connect your own infrastructure guide referenced in the introduction.
An App should generally represent the entire deployment of an application - regardless of whether you're deploying a microservice architecture or a monolith. Within the App you can define individual Workloads. A Workload in Humanitec is analogous to a workload in Kubernetes and represents an individual component of an App.

The Sample Images

Along with the pre-configured infrastructure, we've also provided two sample Docker images to help you get started:
  • sample-app a React-based task list web app, and
  • sample-service a NodeJS-based API service that uses a Postgres database to store the tasks.
Architecture of sample App provided by Humanitec - Showing: Web Client -> Sample App -> Sample Service -> Postgres Database
If you're interested in looking at the code of the docker images we have provided, you can find it over at the Humanitec Tutorials Github organization. Do bear in mind, however, that this code is not actively maintained and is not to be considered an example of production quality code.

Create an App in Humanitec

The first step to defining a new App in Humanitec is to create an entity where you can register the components.
UI
API
A Screenshot of the App Overview Screen - Showing: A Card with the label "Create new app"
1. If you're not on it already, navigate to the App Overview Screen - This is the default screen when you log in or click the Humanitec logo.
2. In the card labeled Create new app, enter a name for your App, and click the Create button. This will open the App Details Screen.
To follow this guide, you'll need a few pre-requisites ready:
Programs used:
Program name
Description
curl
An application for making HTTP requests from the terminal
jq
An application for extracting variables from JSON data in the terminal
Required variables:
Variable
Description
HUMANITEC_ORG
The lowercase representation of your organization's name (e.g., my-org)
HUMANITEC_TOKEN
A Humanitec API bearer token that has the Administrator role on ORG (Learn more about tokens)
1. To register a new App, you'll need to make a POST request to the https://api.humanitec.io/orgs/{orgId}/apps endpoint supplying a JSON object that contains an id field (lowercase name) and a name field (Human-friendly name). E.G.
export HUMANITEC_ORG="my-org"
export HUMANITEC_APP="my-app"
export HUMANITEC_TOKEN="my-token"
curl -X POST https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<EOF
{
"id": "${HUMANITEC_APP}",
"name": "My Application"
}
EOF
In broad strokes, the Humanitec platform has 6 key elements:
  • Environments - An Environment is a space where an instance of an Application can be deployed (a Kubernetes namespace, database services, DNS, etc.)
  • Deployments - A state change to the environment triggered by deploying a particular Set.
  • Sets - A Set contains all the developer-owned environment agnostic configuration for an Application.
  • Deltas - A change definition that specifies how to alter a particular Set - resulting in a new Set.
  • Workloads - A Workload, somewhat analogous to a Kubernetes workload, represents code that will run in a Kubernetes cluster. In the current API version, this is sometimes called a module; this will be deprecated in the next major version of the API - replacing the name modules with workloads.
  • Resources - A Resource is something that a Workload will consume. Examples include Database services (like a Google CloudSQL instance), DNS Services (like Route53), etc.
UI
API

App Details

An annotated Screenshot of the App Details Screen - Showing: An empty Draft Deployment
In this new App, you'll see the screen is composed of 3 main parts:
(A) Environments - An Environment is a space where an instance of an Application can be deployed (a Kubernetes namespace, database services, DNS, etc.)
(B) Deployments (within the selected Environment) - A Deployment is a change to the state of an Environment such as the deployment of a Workload
(C) Workloads & Shared Resources (within the selected Deployment) - Workloads, being somewhat analogous to Kubernetes workloads, represent code that will run in a Kubernetes cluster. Shared Resources are (generally external) services consumed by multiple workloads (such as a shared database).
In this view, you should see a small blue card, selected automatically, for an environment called Development. Below that card, you'll see one for an "Untitled draft" deployment. On the right, you'll see the empty Workloads & Resources section.
You'll learn about adding more environments in the guide on Connect your own infrastructure, but for now, you can proceed with the existing Development environment.

Creating a Delta

With that explained, it's time to create the initial Delta.
2. To do this, you'll be making a POST request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas endpoint with a JSON object that contains an empty modules object, an empty metadata object, and an empty shared array. E.G.
export HUMANITEC_DELTA=`curl -X POST https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
{
"metadata": {},
"modules": {},
"shared": []
}
EOF
This will create the first Delta and returns an id.
From this point on, we'll refer to this id as deltaId.
Strictly speaking, the following steps (where you will PATCH this Delta) could have been rolled up into the original POST request, but to better explain each component, we've split it into dedicated steps.

Add Workloads

As mentioned, Workloads in Humanitec can map to different Profiles. You'll typically want default-module which is designed for stateless workloads (for example API services with external databases). You can find out about other profiles here.
In this case, you'll want to create two Workloads (one for the Sample-App and one for the Sample-Service).

Sample-Service

UI
API
3. From the App Details Screen, click the Create new workload button and enter sample-service as the id when prompted.
4. Click the Create workload button to accept.
A Screenshot of the App Details Screen - Showing: A Draft Deployment containing the sample-service workload
3. Adding a Workload to a Delta requires making a PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint supplying an array of JSON objects that contain any alterations to the metadata and modules objects and/or shared array. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"add": {
"sample-service": {
"profile": "humanitec/default-module",
"spec":{}
}
}
}
}
]
EOF
In this example, we're adding a new empty Workload with the name sample-service using the humanitec/default-module profile. Notice, however, that we've left the spec object empty, we'll define this in the next step.
Note: The Workload name cannot be changed once set, so it's best to make it descriptive.
UI
API
Now that you have an empty Workload it's time to define the App.
5. Click on the newly created sample-service card to enter the Workload Details Screen.
6. The first thing that you'll need here is the container image, to add this click + Create new container which will open a modal dialog box.
7. Select the sample-service image from the dropdown and click the Create button.
A Screenshot of the Workload Details Screen - Showing: An empty Workload specification
The PATCH method implements JSON Patch - RFC6902 for the modules.updates.{workloadId} path. Allowing you to update the sub-tree without re-submitting the entire object.
4. Adding a Container to a Workload requires making another PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-service": [
{
"op": "add",
"path": "/spec/containers/sample-service",
"value": {
"files": {},
"id": "sample-service",
"image": "registry.humanitec.io/public/sample-service:1.2.0",
"resources": {
"limits": {
"cpu": "0.250",
"memory": "256Mi"
},
"requests": {
"cpu": "0.250",
"memory": "256Mi"
}
"variables": {},
"volume_mounts": {}
}
}
}
]
}
}
}
]
EOF
Again, this step could have been rolled up in the earlier request.
Notice the op, path, and value fields in the example, as per the JSON Patch standard: Humanitec allows you to specify changes at the sub-tree level by defining a list of operations to save you from needing to pass the entire object. Each operation has an op (I.E. add/update/delete), a path (where to perform the change) rooted at the workload object, and a value (the object to add or update, which can be omitted in the case of a delete).
In this example, you're adding a Container to the spec object that was left blank in the previous step. However, this time, you're creating the container with the id sample-service using the registry.humanitec.io/public/sample-service:1.2.0 image. As part of this container specification, we also define some default Kubernetes-style resource requirements. To do this, you're injecting it at the /spec/containers/sample-service sub-tree path; thus, its expanded path would be /modules/sample-service/spec/containers/sample-service. This is because the path is rooted at the Workload object, I.E., /modules/sample-service.
So far, this has been very similar to defining a Kubernetes Deployment manifest, but it's about to get a bit more interesting.
Given that this service requires a Postgres database, you'll need to add that to the workload definition.
UI
API
8. You can define that requirement via the Resource dependencies card. From the dropdown, select Postgres.
9. This will open a modal dialog box where you can give the resource reference a name and call it my-db.
A zoomed-in Screenshot of the Workload Details Screen - Showing: The Add resource dropdown menu
5. To add a Resource dependency to a Workload make another PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-service": [
{
"op": "add",
"path": "/externals/my-db",
"value": {
"type": "portgres"
}
}
}
]
}
}
}
]
EOF
Again, this step could have been rolled up in the earlier request.
Now you need to let the sample-service Workload know where to find the database. To do this, you'll need to define an Environment Variable to be passed to the container.
Notice that while you've told Humanitec that you need a Postgres database, you haven't needed to define how to implement that. Thanks to Dynamic Configuration Management, this is because you don't need to. This will be resolved automatically at launch time by Humanitec using the Resource Definitions for the relevant Environment.
You can find out more about this in the Connect your own infrastructure series.
UI
API
7. To create a new variable, click on the + Add Variables button
8. Our Sample-Service image expects the Postgres connection string to be supplied as an environment variable called CONNECTION_STRING, so that's what you'll need to put in the Key field.
In the Value field, you will need to use Placeholders that the correct credentials will replace at launch time.
9. In this case, you'll want to build the connection string as follows postgresql://${externals.my-db.username}:${externals.my-db.password}@${externals.my-db.host}:${externals.my-db.port}/${externals.my-db.name}
10. Press the Add button next to the end of the editable field to save.
A Screenshot of the Workload Details Screen - Showing: A new variable being written
6. To add the Environment variable to the container, you'll need to make another PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-service": [
{
"op": "add",
"path": "/spec/containers/sample-service/variables/CONNECTION_STRING",
"value": "postgresql://${externals.my-db.username}:${externals.my-db.password}@${externals.my-db.host}:${externals.my-db.port}/${externals.my-db.name}"
}
}
]
}
}
}
]
EOF
Again, this step could have been rolled up in the earlier request.
In this connection string, you'll likely have noticed that the Placeholders have the structure ${<source>.<object_id>.<variable>} - Where <source> in this case is "externals", and the <object_id> is "my-db" (the name you gave to the Postgres resource). You can learn more about the available Placeholders for each integration from the resource types documentation.
UI
API
Now that you're done setting up the Sample-Service it's time to head back to the App Details page and add the next Workload.
11. Click the < Back to app details button in the top right.
As mentioned above, these API calls could have been rolled into either a single PATCH or the original POST. Rolling up this into the original POST would have looked something like this:
curl -X POST https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
{
"metadata": {
"env_id": "development"
},
"modules": {
"add": {
"sample-service": {
"externals": {
"my-db": {
"type":"postgres"
}
},
"profile": "humanitec/default-module",
"spec": {
"containers": {
"sample-service": {
"id": "sample-service",
"image": "registry.humanitec.io/public/sample-service:1.2.0",
"resources": {
"limits": {
"cpu": "0.250",
"memory": "256Mi"
},
"requests": {
"cpu": "0.025",
"memory": "64Mi"
}
},
"variables": {
"CONNECTION_STRING": "postgresql://${externals.my-db.username}:${externals.my-db.password}@${externals.my-db.host}:${externals.my-db.port}/${externals.my-db.name}"
}
}
}
}
}
}
}
}
EOF

Sample-App

UI
API
12. Once again, click Create new workload and enter the name sample-app when prompted.
13. Then click the Create workload button to accept.
14. Click on the newly created sample-app card to enter the Workload Details Screen.
7. Once again, to add a Workload to a Delta, you'll need to make a PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint supplying an array of JSON objects. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"add": {
"sample-app": {
"profile": "humanitec/default-module",
"spec":{}
}
}
}
}
]
EOF
Now that you have another empty Workload, it's time to define the app.
Again, the container image is the first thing you'll need here.
UI
API
15. To add this, click + Create new container, which will open a modal dialog box.
16. Select the sample-app image from the dropdown and click the Create button.
8. As before, add the container to the new Workload via a PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-app": [
{
"op": "add",
"path": "/spec/containers/sample-app",
"value": {
"files": {},
"id": "sample-service",
"image": "registry.humanitec.io/public/sample-app:1.3.0",
"resources": {
"limits": {
"cpu": "0.250",
"memory": "256Mi"
},
"requests": {
"cpu": "0.250",
"memory": "256Mi"
}
"variables": {},
"volume_mounts": {}
}
}
}
]
}
}
}
]
EOF
Now you need to tell the Sample-App how to find the Sample-Service. Similar to the sample-service image, the sample-app image accepts an environment variable.
UI
API
17. In the Variables section add a new Environment Variable with the Key PUBLIC_SERVER_URL and the Value http://${modules.sample-service.service.name}:8080. Make sure to click the Add button to save this.
This Environment Variable's Placeholder is very similar to a Kubernetes service name and will be resolved at launch.
A Screenshot of the Workload Details Screen - Showing: A new variable being written
9. Again, add the Environment variable to the container via a PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-app": [
{
"op": "add",
"path": "/spec/containers/sample-app/variables/PUBLIC_SERVER_URL",
"value": "http://${modules.sample-service.service.name}:8080"
}
}
]
}
}
}
]
EOF
Next, you'll need to add an Ingress solution.
UI
API
18. To do this, you'll need to select a DNS Resource from the Resource dependencies dropdown.
19. In the modal dialog that this opens, you need to specify a name for the DNS service; for now, use my-dns.
20. Next, in the Ingress card's dropdown, you'll find the Resource you just created; clicking on that opens up another modal dialog.
21. In this modal dialog, you have a few Path types to choose from (Exact, Prefix, ImplementationSpecific, and Default); you can find out more about them here.
A Screenshot of the Workload Details Screen - Showing: A new ingress route being added
22. In this case, you'll select Default, which means that all traffic at the URL will arrive at the Workload
23. In the Port field, you'll put 8080, as this is the port on which the sample-app docker image is listening.
Adding an Ingress requires that there is also a matching Resource dependency available to the Workload.
10. To do this, make a single PATCH request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/deltas/{deltaId} endpoint. E.G.
curl -X PATCH https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/deltas/${HUMANITEC_DELTA}
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
[
{
"modules": {
"update": {
"sample-app": [
{
"op": "add",
"path": "/externals/my-dns",
"value": {
"type": "dns"
}
}
},
{
"op": "add",
"path": "/spec/ingress/rules/externals.my-dns/http/*",
"value": {
"type": "default",
"port": 8080
}
}
]
}
}
}
]
EOF

Deploy the Application

You are now ready to deploy your first app.
UI
API
24. Click the Deploy button.
25. Enter a Comment in the respective input field and hit Deploy.
Running Sample App with two Workloads in the App Details Screen
When your App finishes deploying, the Development environment will be marked as Successful, and the Active deployment will be shown as Running.
11. To deploy the Delta you'll need to make a POST request to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/envs/{envId}/deploys endpoint with an object containing a delta_id item and a comment item. E.G.
export HUMANITEC_DEPLOY=`curl -X POST https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/${HUMANITEC_APP}/envs/development/deploys
-H 'Content-Type: application/json'
-H 'Authorization: Bearer ${HUMANITEC_TOKEN}'
-d @- <<'EOF'
{
"delta_id": deltaId,
"comment": "Initial Deployment"
}
EOF | jq '.id'
This will return an object with an id and additional information about the deployment.
UI
API
You can now visit the frontend of your newly created App in a browser. To do this, you can retrieve the DNS name for the service as follows:
26. Select the Active deployment in the left-hand column.
27. Click on the sample-app card to open the Workload Details Screen.
28. The generated DNS is available to copy in the Ingress section.
29. Enter the copied URL in a new browser tab and check out the running app.
12. To check on the status of the deployment you can poll the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/envs/{envId}/deploys/{deployId} endpoint with a GET request. This request will return a similar object to the POST but with the latest information.
13. Once the status value is "succeeded", you can make a GET to the https://api.humanitec.io/orgs/{orgId}/apps/{appId}/envs/{envId}/resources endpoint, which will return a list containing each resource provisioned in the current active deployment, one of which will be the dns Resource defined in Step 10.

All deployed!

You've deployed your first App in Humanitec - Fantastic! Of course, this app is running on the demo infrastructure provided by Humanitec (as apparent from the *.newapp.io subdomain).
If you're a Platform Engineer, you probably want to head on over to the Connect your own infrastructure guide to getting it up and running on your infrastructure.
If you're a Developer, you might be more interested in the Configuring your CI/CD pipeline or perhaps Migrating your own app.

Next steps

Copy link
On this page
Introduction to Application Deployments
What is an App in Humanitec
The Sample Images
Create an App in Humanitec
Add Workloads
Deploy the Application
All deployed!
Next steps