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 namedTEST
containing1234
.
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).