Run Backstage on Ubuntu 24.04 Using Docker

This guide shows a simple, production-style Docker deployment for a Backstage app that we previously ran from source.

Goal:

  • Build a single Docker image (backstage:local)
  • Inject secrets via --env-file .env
  • Persist SQLite on the host at /var/lib/backstage/db

Prerequisites

  • Docker installed on Ubuntu 24.04
  • Backstage repo checked out on the server (or build machine)
  • A working app-config.production.yaml

Notes:

  • SQLite is fine for a single instance. If you plan multiple replicas, use Postgres instead.
  • If you already have HTTPS (NGINX/Traefik) in front of Backstage, keep using it.

  1. Production config + environment variables

For Docker / K8s, keep production settings in app-config.production.yaml and inject secrets using environment variables.

  • Bind the backend to all interfaces

In app-config.production.yaml:

app:
  baseUrl: https://backstage.maksonlee.com

backend:
  baseUrl: https://backstage.maksonlee.com
  listen: ":7007"

database:
  client: better-sqlite3
  connection:
    directory: /var/lib/backstage/db

Make sure the backend is not bound to 127.0.0.1 inside the container.

Use listen: “:7007” (or “0.0.0.0:7007”) so Docker port mapping and HAProxy forwarding can reach it.

  • Create .env

Create the env file in your repo root:

vi .env

Put all required environment variables there (secrets, tokens, passwords, etc.). Example:

AUTH_SESSION_SECRET=xxxxxxxxxxxxxxxx

AUTH_OIDC_CLIENT_ID=xxxxxxxxxxxxxxxx
AUTH_OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxx

LDAP_BIND_PASSWORD=xxxxxxxxxxxxxxxx

GITHUB_TOKEN=xxxxxxxxxxxxxxxx
GERRIT_USERNAME=xxxxxxxxxxxxxxxx
GERRIT_TOKEN=xxxxxxxxxxxxxxxx

JENKINS_USERNAME=xxxxxxxxxxxxxxxx
JENKINS_API_TOKEN=xxxxxxxxxxxxxxxx

ARTIFACTORY_AUTH_HEADER=xxxxxxxxxxxxxxxx

In app-config.production.yaml, reference env vars using ${...}.


  1. Build the Docker image
yarn install --immutable
yarn tsc
yarn build:backend
docker build -f packages/backend/Dockerfile -t backstage:local .

That’s the standard flow:

  • install deps
  • compile TypeScript
  • build the backend bundle
  • build the Docker image

  1. Persist SQLite to /var/lib/backstage/db

On the host:

sudo mkdir -p /var/lib/backstage/db

# Check which UID the container runs as
docker run --rm backstage:local id

# Example: uid=1000 gid=1000
# Use the numbers you see:
sudo chown -R <uid>:<gid> /var/lib/backstage/db

This ensures the container user can write the SQLite files.


  1. Run Backstage with Docker

Run:

docker run --rm \
  -p 7007:7007 \
  --env-file .env \
  -v "/var/lib/backstage/db:/var/lib/backstage/db" \
  backstage:local

Access paths

  • Direct (no reverse proxy):
    http://<server-ip>:7007
  • Production (behind HAProxy with TLS termination):
    https://backstage.maksonlee.com (HAProxy listens on 443 and forwards to :7007)

  1. Updating procedure
  • Code changes (plugins / backend / frontend)

Rebuild the image and restart the container:

yarn install --immutable
yarn tsc
yarn build:backend
docker build -f packages/backend/Dockerfile -t backstage:local .

Then run the container again:

docker run --rm \
  -p 7007:7007 \
  --env-file .env \
  -v "/var/lib/backstage/db:/var/lib/backstage/db" \
  backstage:local
  • Config-only changes

If you changed anything in app-config.production.yaml, rebuild the image (same commands above), then run again.

Did this guide save you time?

Support this site

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top