Best practices #

Before you start creating builders and deploying new projects, be sure to keep in mind the following:


  • Your server runtime code must use the same Nakama version as the selected Nakama image for the builder.
  • In Heroic Cloud, Nakama is always started with the runtime.path parameter set to /nakama/data/modules.
  • You have to manually trigger builders to create new images, and manually update your project to use the new image, neither action is automated in Heroic Cloud.


  • All server runtime code must live in the repository root directory.
  • The repository should only contain source code relevant for the server module (i.e. do not place your Unity project there).
  • Beside your custom runtime code files, all other files will be removed from the builder except for the following extensions: .yml, .json, .txt, and .md.

TypeScript #

  • You must include the (single) compiled bundled JavaScript file, not TypeScript.
  • In your Project configuration, set the runtime.js_entrypoint parameter to the location of your bundled JS file (e.g. build/index.js).
    • If a relative path is used, remember that it is relative to the configured runtime.path.
  • After pushing any changes to your JS bundle, trigger a new build based on that latest commit.

Lua #

  • You must include at least one file in the root directory, all other Lua modules can then be included in subfolders.
  • Builders are designed to ignore your Lua test files as follows:
    • All .lua files in a /tests directory
    • Any files named in the format *.test.lua or *_test.lua

Go #

  • Your main.go and mod files must be in the root directory, all others can be in subfolders.
  • The shared object or Dockerfile do not need to be committed.

Example workflow #

Heroic Cloud can be used in the same manner as the traditional development lifecycle where there are multiple environments - Development, QA, and Production - with new features promoted through the hierarchy of these environments as they are developed, validated, and released.

This can be done as follows:

  1. Create an organization for each application being developed. For example, let’s say we are building a game called Sagi-shi.
  2. Within this organization, create three projects to represent our three environments: sagi-shi-dev, sagi-shi-qa, and sagi-shi-prod.
  3. Create a builder for Sagi-shi. This builder will be linked to the repository with the custom server runtime code for our game.
  4. When some new code/feature is pushed to this repository we can trigger a new build to bundle this new code with our selected Nakama image into a Docker image.
  5. This new image can then be deployed to the desired project (environment). For example sagi-shi-dev to start.
  6. Steps 4-5 are repeated as new code is pushed and validated, new images built and tested and, ultimately, deployed to the sagi-shi-qa and then sagi-shi-prod projects.

Deploying new server code #

When new server code is pushed to the repository, a new build must be triggered to create a new Docker image. This image can then be deployed to any desired project.

New builds are not automatically triggered with new commits to the connected repository, they must be started manually.

To trigger a new build:

  1. Navigate to the Trigger tab of the desired builder.
  2. It is good practice to use the Refresh Commits button to ensure all recent activity in the connected repository is reflected.
  3. Using the input field or provided table select the Commit SHA to use for the new build.
  4. Click Trigger to start building the new image.
  5. From the Info tab you can watch the builder status as it completes.
Deploying New Builds
Once a new image is successfully built, it is not automatically deployed to any project. The new image must be manually deployed from the desired Project configuration.

Handling rolling reboots #

A rolling reboot is performed whenever a project is updated to a new image, and can also be manually triggered from the Project Deployment tab.

Match migration can be used to avoid players being unexpectedly disconnected from the game mid-match.