Skip to main content
Version: 1.11.0

Deploying a complex application

In this tutorial you use Gitea to work through a more complex application with Epinio.

Gitea is a self-hosted Git service, written in Go and Node.js.

At the time of writing the Paketo Go buildpack doesn't support Node.js asset compilation, (see issue #671), so you need to create a custom builder.

The builder checks the needed Go and Node dependencies, and executes the make command to build both the front end and the back end.

The example code used during this tutorial is available in the example-builder-gitea repository.

git clone
git submodule update --init --recursive
cd example-builder-gitea

Basic concepts and prerequisites

Before starting, it's useful to understand the buildpacks concepts and have a few tools already installed.

The buildpack documentation describes:

You should also have pack installed, along with docker and skopeo (to manage Open Container Initiative (OCI) artifacts).

Create the stack

It's impossible to reuse the Paketo Full Stack because of a missing dependency in the run image. To make it easy there is a forked stack and added git to the run packages (see 408e949).


We had to fork and reuse the same stack ID so that it's possible to reuse certain Paketo buildpacks. They would work only with these stacks (see buildpack.toml#L29).

Build the stack and publish the image

To build the stack:


This creates two Open Container Initiative (OCI) images in the stacks/bionic-full-stack/build folder:

  • build.oci
  • run.oci

From this folder you can un-tar the OCI artifacts:

cd stacks/bionic-full-stack/build
mkdir build && tar -xf build.oci -C build
mkdir run && tar -xf run.oci -C run

Use skopeo to copy the artifacts into the local Docker registry:

skopeo -v copy oci:build<username>/bionic-full-stack-build:0.1.0
skopeo -v copy oci:run<username>/bionic-full-stack-run:0.1.0

Push them to a public registry:

docker push<username>/bionic-full-stack-build:0.1.0
docker push<username>/bionic-full-stack-run:0.1.0

Creating the make buildpack

To build the Gitea application we created a make buildpack.


During the detect phase of the bin/detect script it checks the needed dependencies.

It checks for the existence of the Makefile:

if [[ ! -f Makefile ]]; then
echo Makefile not found
exit 100

It writes to the plan.toml file that this buildpack requires Go and Node.js:

cat >> "${plan_path}" <<EOL
name = "node"
# version = "*"

build = true

name = "go"

build = true


During the build phase the buildpack runs make to build the application:

TAGS="bindata" make build

Set up the app configuration changing the listening port in the app.ini file:

cp custom/conf/app.example.ini custom/conf/app.ini
sed -i "s/;HTTP_PORT = 3000/HTTP_PORT = 8080/" custom/conf/app.ini

Define the launch process in the launch.toml file:

cat >> "${layers_dir}/launch.toml" <<EOL
type = "web"
command = "./gitea"
default = true

Create and publish the builder

The builder.toml file has the buildpack references and their execution order/groups.

You reference the make buildpack locally:

id = "make"
version = "0.0.1"
uri = "../../buildpacks/make"

Also, you use the Paketo node-engine and go-dist Buildpacks:

uri = "docker://"
version = "1.3.0"

uri = "docker://"
version = "2.3.0"

These buildpacks contain the Node.js and Go dependencies:

Finally, create the builder image with pack:

pack builder create<username>/gitea-builder:0.1.0 --config builders/gitea-builder/builder.toml

Push it to a public registry:

docker push<username>/gitea-builder:0.1.0

Deploy Gitea

To deploy Gitea you can download the code from the repository, or directly refer to it.

You need to deploy a stable release, so find the commit of the v1.19.0 with a couple of curls:

export GITEA_VERSION=v1.19.0
export GITEA_TAG_OBJECT_URL=$(curl -s$GITEA_VERSION | jq -r '.object.url')
export GITEA_TAG_COMMIT_SHA=$(curl -s $GITEA_TAG_OBJECT_URL | jq -r '.object.sha')

You can now deploy Gitea with a simple epinio push:

epinio push --name gitea \

It should be available on your cluster (that is, https://gitea.<SYSTEM_DOMAIN>).

Create and bind the database

You can go a step further, creating and binding the mysql database to your application:

epinio service create mysql-dev mydb

Check the status of the service:

-> % epinio service show mydb

🚢 Showing Service...

✔️ Details:
| Name | mydb |
| Created | 2023-04-04 15:56:20 +0200 CEST |
| Catalog Service | mysql-dev |
| Version | 8.0.31 |
| Status | deployed |
| Used-By | |
| Internal Routes | xcca9aa0f19a036fb6389474a7be0-mysql-headless.workspace.svc.cluster.local:3306, |
| | xcca9aa0f19a036fb6389474a7be0-mysql.workspace.svc.cluster.local:3306 |

You can use the internal route xcca9aa0f19a036fb6389474a7be0-mysql.workspace.svc.cluster.local:3306 to reach your DB.

Bind it to the Gitea application with epinio service bind mydb gitea.

Now you check the username and passwords in the configuration:

-> % epinio configuration list

🚢 Listing configurations
Namespace: workspace

✔️ Epinio Configurations:
| xcca9aa0f19a036fb6389474a7be0-mysql | 2023-04-04 15:56:22 +0200 CEST | service | mydb | gitea (migrate to new access |
| | | | | paths) |


-> % epinio configuration show xcca9aa0f19a036fb6389474a7be0-mysql

🚢 Configuration Details
Name: xcca9aa0f19a036fb6389474a7be0-mysql
Namespace: workspace

Created: 2023-04-04 15:56:22 +0200 CEST
Type: service
Origin: mydb
Used-By: gitea

⚠️ Attention: Migrate bound apps to new access paths
| mysql-password | 6oUDWVHVcv | /configurations/mydb/mysql-password |
| mysql-root-password | eG83csnOLe | /configurations/mydb/mysql-root-password |

⚠️ Beware, the shown access paths are only available in the application's container

You can use these credential to access the database. Access the first time Gitea configuration from your browser and fill the database fields with the host, username, and password. The database name is my_database, it's the default value for Bitnami charts that Epinio uses for its example services.