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.
- 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 ${...}.
- 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
- 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.
- 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)
- 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