From GitLab to Heroku with Docker

mattdark profile image Mario García Updated on ・5 min read

From Zero to Production with Rust, Python and GitLab (4 Part Series)

1) Development Environment for Rust and Python on Linux 2) A web app built with Rust and Python 3) A custom Docker image for Rust and Python 4) From GitLab to Heroku with Docker

After writing the code of the project and testing it in a local development environment, you must create a GitLab repository and upload the code and configure to deploy to Heroku.


Add a .gitignore file to your repository so the temporary files and directories generated during the building process don't be uploaded to GitLab. You can use the templates provided by GitLab or generate this file at gitignore.io.


Just type in the search bar the technologies you're using for the project and it will generate the file for you.

The file will look as follows:

# Created by https://www.gitignore.io/api/rust,python
# Edit at https://www.gitignore.io/?templates=rust,python

### Python ###
# Byte-compiled / optimized / DLL files

# C extensions

# Distribution / packaging

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.

# Installer logs

# Unit test / coverage reports

# Translations

# Scrapy stuff:

# Sphinx documentation

# PyBuilder

# pyenv

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.

# celery beat schedule file

# SageMath parsed files

# Spyder project settings

# Rope project settings

# Mr Developer

# mkdocs documentation

# mypy

# Pyre type checker

### Rust ###
# Generated by Cargo
# will have compiled files and executables

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html

# These are backup files generated by rustfmt

# End of https://www.gitignore.io/api/rust,python


Before configuring the repository, you should create a Heroku app.
A new Heroku app

Go to dashboard.heroku.com/account and copy the API Key.


Create a new Dockerfile in your repository. it will have the following content:

FROM docker.io/username/rust-python:latest

WORKDIR /home/admin
USER admin
RUN mkdir app

RUN env PYTHON_CONFIGURE_OPTS='--enable-shared' pyenv install 3.7.7
ENV LD_LIBRARY_PATH $HOME/.pyenv/versions/3.7.7/lib/

RUN pyenv global 3.7.7
RUN pyenv rehash

RUN rustup override set nightly

COPY pyproject.toml ./

RUN poetry config virtualenvs.create false \
    && poetry lock \
    && poetry export --without-hashes -f requirements.txt --dev \
    |  poetry run pip install -r /dev/stdin \
    && poetry debug

COPY  . ./

RUN poetry install --no-interaction \
    && cargo build --release

CMD ["sh", "run"]

According to this issue, the container registry address (docker.io) must be specified before the name of the base image, for buildah to pull the image correctly from Docker Hub.

The base image used for the project will be the one created before and available from username/rust-python:latest.

Then set the WORKDIR to /home/admin and the USER to admin. Create the app directory and set it as WORKDIR.

Install Python 3.7.7 with shared libraries and set the LD_LIBRARY_PATH environment variable.

Specify the version of Python and Rust to be used.

Copy the pyproject.toml file. Poetry is being used to manage the dependencies of Python but no virtual environment will be created.

Then lock the dependencies and export to a requirements.txt file and install them using pip, this is done to generate a cache of them.

Then install Python and Rust dependencies of the project, and build the project for production.

When the Docker image being created is run, the bash script named run is executed.

The run bash script has the following content, so that Heroku knows how to run the app:

ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/rust-python-demo

The variable $PORT is set by Heroku as it dynamically chooses a port for the app, this value is assigned to ROCKET_PORT. Then the environment is set to production with ROCKET_ENV=prod and the binary of the app is run.

GitLab CI

Add the value of the API Key of Heroku that you copied before to the settings of the repository by following the steps bellow:

  • Go to Settings > CI/CD.
  • In the Variables section click on Expand.
  • Add the variable HEROKU_API_KEY and assign the value copied before.

Heroku API Key

Then add the .gitlab-ci.yml file, this is the configuration file of the Continuous Integration system provided by GitLab.

For this I just follow Alessandro Diaferia's tutorial on Continuous Deployment With GitLab, Docker And Heroku.

The file will look as follows:

  - build
  - release

    - master
  image: registry.gitlab.com/majorhayden/container-buildah
  stage: build
    BUILDAH_FORMAT: "docker"
    - dnf install -y nodejs
    - curl https://cli-assets.heroku.com/install.sh | sh
    - sed -i '/^mountopt =.*/d' /etc/containers/storage.conf
    - buildah bud --iidfile iidfile -t rust-python-demo:$CI_COMMIT_SHORT_SHA .
    - buildah push --creds=_:$(heroku auth:token) $(cat iidfile) registry.heroku.com/heroku-app-name /web

    - master
  image: node:10.17-alpine
  stage: release
    - apk add curl bash
    - curl https://cli-assets.heroku.com/install.sh | sh
    - heroku container:release -a heroku-app-name web

It will only have two stages, build_image, where the Docker image is built using the Dockerfile created previously and added to the registry of Heroku, and release, where the image will be pushed to the Heroku app.

Just replace registry.heroku.com/heroku-app-name/web and heroku container:release -a heroku-app-name web with the name of the app you created on Heroku.

If everything goes well, the jobs of the pipeline will succeed and your app is deployed to Heroku.
GitLab Pipeline

You can go to rust-python-demo.herokuapp.com to see a live demo running.
Heroku App running on Firefox

From Zero to Production with Rust, Python and GitLab (4 Part Series)

1) Development Environment for Rust and Python on Linux 2) A web app built with Rust and Python 3) A custom Docker image for Rust and Python 4) From GitLab to Heroku with Docker

Posted on by:

mattdark profile

Mario García


Free Software Enthusiast | Speaker | Mozillian | Mozilla Reps | Mozilla Open Leaders | Python & Rust Developer


markdown guide