Use a Custom Buildpack

If you need to deploy a technology which is not supported by Scalingo, you can use an open source buildpack or a buildpack you have developed.

To achieve this, you must define the following environment variable in the environment of the concerned application: BUILDPACK_URL.

Example:

BUILDPACK_URL="https://github.com/cloudfoundry/java-buildpack"

Then the deployment logs contain:

<-- Start deployment of my-app -->
-----> Cloning custom buildpack: 'https://github.com/cloudfoundry/java-buildpack'

-- SNIP --

If you need to test a branch of a custom buildpack different than main (or master), specify its name at the end of the URL:

BUILDPACK_URL="https://github.com/cloudfoundry/java-buildpack#branch_name"

Build a Custom Buildpack

Our execution stack is public and can be found as a Docker image on Docker Hub under the name scalingo/scalingo-22. The base image is based on Ubuntu 22.04. The Scalingo stack contains every tool from the ubuntu:22.04 Docker image.

Run the following command to start a container in the same environment a custom buildpack will be executed:

docker run --pull always --rm --interactive --tty --env STACK=scalingo-22 --volume /path/to/custom-buildpack:/buildpack --volume /path/to/application:/build scalingo/scalingo-22:latest bash

Then, export the environment variables from your application into the Docker container. Inside the Docker container, create the folders that will be used by the buildpack scripts:

mkdir /tmp/{cache,env}

Building a third-party binary inside this container ensures it will work in a Scalingo application.

Architecture of a Buildpack

A buildpack has three mandatory entrypoints:

  • bin/detect: exit with success (return code is 0) if the buildpack applies to the current application.
  • bin/compile: installs the technology.
  • bin/release: handles some metadata.

All these entrypoints are usually Bash script.

Script detect

The purpose of the script located in bin/detect is to detect if the buildpack applies to the application. It takes the build directory in argument. If the buildpack is applicable, the script must print on the standard output the name of the technology and return with the code 0.

Here is an example of such script which detects a buildpack as applicable for the Rust programming language if the file Cargo.toml is present at the root of the application.

#!/usr/bin/env bash

BUILD_DIR=${1:-}

if [ -f "${BUILD_DIR}/Cargo.toml" ]; then
  echo "Rust"
  exit 0
fi

exit 1

Script compile

The purpose of the script located in bin/compile is to actually compile the application. It is called with three arguments:

  • The build directory: contains the code of the application.
  • The cache directory: used to store information one wants to keep between two builds.
  • The environment directory: contains a file per environment variable defined. For instance, an environment variable TEST=1234 leads to a file named TEST containing 1234.

Here is an example of a (very basic) compile script to compile a Rust application:

#!/usr/bin/env bash

set -e

BUILD_DIR=${1:-}
CACHE_DIR=${2:-}
ENV_DIR=${3:-}

# Install rustup in the cache directory.
# rustup installs rust compiler (rustc) and package manager (Cargo).
export RUSTUP_HOME="$CACHE_DIR/rustup"

# Configure rustup to install Cargo in cache directory.
# Cargo holds Rust tools executables and the downloaded packages.
export CARGO_HOME="$CACHE_DIR/cargo"

export PATH="$CARGO_HOME/bin:$PATH"

echo "-----> Install Cargo"
cd "$CACHE_DIR"
if [ ! -x "rustup.sh" ]; then
    curl https://sh.rustup.rs -sSf > rustup.sh
    chmod u+x rustup.sh
fi

# Use minimal profile to save disk space.
# Mininal profile provides rust compiler (rustc) and package manager (Cargo).
./rustup.sh -y --profile minimal

echo "-----> Compile the application"
# `cargo install` builds release binaries and copy them in BUILD_DIR/bin directory.
cargo install --path "$BUILD_DIR" --target-dir "$CACHE_DIR/target" --root "$BUILD_DIR"

You can test this script in the Docker container with the command:

/buildpack/bin/compile /build /tmp/cache /tmp/env

When this buildpack is in use in a Scalingo application, at the end of the execution of this script, the content of the BUILD_DIR script is moved to /app. /app is the HOME of the application during the runtime.

Script release

The purpose of the script located in bin/release is to generate some metadata about the application. It takes the build directory in argument. It must print on the standard output a YAML file with a couple of keys:

  • addons: list of addons to install. It is only applied the first time an application is deployed.
  • config_vars: hash of default environment variables.
  • default_process_types: hash of default Procfile entries.

Here is an example of such script for a Rust application that would need a PostgreSQL database:

#!/usr/bin/env bash

cat << EOF
---
addons:
  - scalingo-postgresql
config_vars:
  PATH: /app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
EOF

By default, the PATH of an application contains the following value: /app/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/app/bin.

If no default_process_types is provided in the release script, buildpack users need to add a Procfile to instruct Scalingo how to boot their application. In the case of a Rust application, it should contain the path to the executable compiled by the compile script. For example:

web: /app/bin/my_app

Script .profile.d

During startup of any container types, the container starts a Bash shell that sources all the files in the .profile.d/ folder, before executing the container start command. This allows a buildpack to customize the environment of the application. You can take inspiration of what has been done in Scalingo PHP buildpack with this folder.

Feel free to take inspiration from the various buildpacks proposed by Scalingo.

Private Buildpack

If the URL of BUILDPACK_URL is ending with .tar.gz or .tgz, Scalingo downloads and extracts this archive. This allows you to host a private buildpack archive. It may typically contain a secret hash inside the URL (e.g. https://myhostingsite/secret-hash-with-lot-letters-and-numbers.tgz).


Suggest edits

Use a Custom Buildpack

©2025 Scalingo