Integrating with CI/CD 🔒

How to deploy apps between 2 or more Budibase instances

A common need among Budibase users is the ability to deploy two or more segregated Budibase environments and migrate app changes between them.

You can set up this deployment process using the following steps, using the Budibase API. Note that importing and exporting API endpoints are not available on free or open-source tiers.

  1. Export the app from the development environment.
  2. Import the app into the production environment.
  3. (Optionally) Publish the app in the production environment. If you don’t publish, then you have the opportunity to test the app in the production environment before manually publishing the app to your users.

We've written a bash script (below) that automates this process. Most CI/CD platforms support bash scripting inside their pipelines. You can use this script directly in your CI/CD process.

Using external database and APIs

If you are using an external database or API (e.g. PostgreSQL), you can use environment variables to enable your production environment to use different connection details to your development environment.

The process is as follows:

  1. Create the required environment variables in your Budibase development instance.
  2. Create environment variables of the same name, in your production instance.
  3. In your development app, create your database connection using the environment variables.

For example, we can add pg_host and pg_password variables in both instances.

Now I can configure my Postgres connection to use these variables.


The result: my development environment connects to a different database than my production environment, without needing to change this manually each time I deploy.

Bash script for CI/CD

Variables

The bash script below uses the following variables. You'll need to supply these variables from your CI/CD pipeline.

Variable nameDescriptionExample
BUDIBASE_SOURCE_TENANTRequired

The root URL of our budibase development instance.
https://dev_instance.budibase.app
BUDIBASE_SOURCE_API_KEYRequired

API Key of your development instance
How to get your API key
BUDIBASE_SOURCE_APP_IDRequired

The app ID that you would like to deploy

You should treat this as a secret.
app_<uuid> - will deploy the published verison of your app.

Or

app_dev_<uuid> - will deploy the latest, unpublish version of your app.

E.g. app_75907e45dbef4fdcbe6f079b3a5f0b2d
BUDIBASE_DESTINATION_TENANTRequired

The root URL of our budibase production instance.
https://prod_instance.budibase.app
BUDIBASE_DESTINATION_API_KEYRequired

API Key of your production instance.

You should treat this as a secret.
How to get your API key
BUDIBASE_DESTINATION_APP_IDRequired

The app ID that you would like to deploy
app_dev_<uuid>
BUDIBASE_PUBLISH_DESTINATION_APPOptional

Whether you'd like to auto-publish your app in production. Defaults to false
trueorfalse

Script (requires cURL)

The script below saves the exported app to export.tar.gz. You may save this file as an artifact during your CI/CD process. The script could also be modified to export the existing app in your destination tenant, to store as an artifact.

#!/bin/bash

# 1. Set local variables from environment variables

# Check that all required variables are set (not empty)
if [[ -z "$BUDIBASE_SOURCE_TENANT" ]]; then
  echo "Error: BUDIBASE_SOURCE_TENANT is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_DESTINATION_TENANT" ]]; then
  echo "Error: BUDIBASE_DESTINATION_TENANT is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_SOURCE_API_KEY" ]]; then
  echo "Error: BUDIBASE_SOURCE_API_KEY is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_DESTINATION_API_KEY" ]]; then
  echo "Error: BUDIBASE_DESTINATION_API_KEY is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_SOURCE_APP_ID" ]]; then
  echo "Error: BUDIBASE_SOURCE_APP_ID is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_DESTINATION_APP_ID" ]]; then
  echo "Error: BUDIBASE_DESTINATION_APP_ID is required but not set."
  exit 1
fi
if [[ -z "$BUDIBASE_PUBLISH_DESTINATION_APP" ]]; then
  BUDIBASE_PUBLISH_DESTINATION_APP="false" # Default to false if not set
fi


# 2. Check for the existence of curl
if ! command -v curl &> /dev/null; then
    echo "curl could not be found."

    # 3. If curl does not exist, download it
    echo "Installing curl..."
    # Check for sudo privileges, as installing packages usually requires them.
    if [[ $EUID -ne 0 ]]; then
       echo "This script must be run as root to install curl."
       exit 1
    fi
    apt-get update && apt-get install -y curl
else
    echo "curl is already installed."
fi

# 4. Use curl to download the app export
echo "Exporting app export from $BUDIBASE_SOURCE_TENANT and app ID: $BUDIBASE_SOURCE_APP_ID..."


echo $BUDIBASE_SOURCE_TENANT/api/public/v1/applications/$BUDIBASE_SOURCE_APP_ID/export


HTTP_STATUS_EXPORT=$(curl \
     --silent \
     --write-out "%{http_code}" \
     --request POST \
     --url $BUDIBASE_SOURCE_TENANT/api/public/v1/applications/$BUDIBASE_SOURCE_APP_ID/export \
     --header 'accept: application/gzip' \
     --header 'content-type: application/json' \
     --header "x-budibase-api-key: $BUDIBASE_SOURCE_API_KEY" \
     --data '{"excludeRows":true}' \
     --output ./export.tar.gz)


echo "Export HTTP status: $HTTP_STATUS_EXPORT"
if [[ "$HTTP_STATUS_EXPORT" -ne 200 ]]; then
    echo "Error: Failed to export app. HTTP status code: $HTTP_STATUS_EXPORT"
    exit 1
else
  echo "App export successful. HTTP status code: $HTTP_STATUS_EXPORT"
  echo "App export saved to export.tar.gz"
fi

# 5. Use curl to import the app export
echo "Importing app export into $BUDIBASE_DESTINATION_TENANT and app ID: $BUDIBASE_DESTINATION_APP_ID..."

HTTP_STATUS_IMPORT=$(curl \
     --silent \
     --output /dev/null \
     --write-out "%{http_code}" \
     --request POST \
     --url $BUDIBASE_DESTINATION_TENANT/api/public/v1/applications/$BUDIBASE_DESTINATION_APP_ID/import \
     --header 'content-type: multipart/form-data' \
     --header "x-budibase-api-key: $BUDIBASE_DESTINATION_API_KEY" \
     --form '[email protected];type=application/gzip')


if [[ "$HTTP_STATUS_IMPORT" -ne 200 && "$HTTP_STATUS_IMPORT" -ne 204  ]]; then
    echo "Error: Failed to import app. HTTP status code: $HTTP_STATUS_IMPORT"
    exit 1
else
    echo "App import successful. HTTP status code: $HTTP_STATUS_IMPORT"
fi

#6 Optionally- publish the app in the destination environment
if [[ "$BUDIBASE_PUBLISH_DESTINATION_APP" == "true" ]]; then
  echo "Publishing destination app with ID: $BUDIBASE_DESTINATION_APP_ID..."
  HTTP_STATUS_PUBLISH=$(curl \
     --silent \
     --output /dev/null \
     --write-out "%{http_code}" \
     --request POST \
     --url $BUDIBASE_DESTINATION_TENANT/api/public/v1/applications/$BUDIBASE_DESTINATION_APP_ID/publish \
     --header 'accept: application/json' \
     --header "x-budibase-api-key: $BUDIBASE_DESTINATION_API_KEY")
  if [[ "$HTTP_STATUS_PUBLISH" -ne 200 ]]; then
      echo "Error: Failed to publish app. HTTP status code: $HTTP_STATUS_PUBLISH. Your new app exists in the destination but is not published. You can publish or revert manually via Budibase."
      exit 1
  else
      echo "Destination app published successfully."
  fi
else
  echo "Skipping publishing of destination app - please publish manually via Budibase."
fi