This page covers installation, initial configuration, networking, upgrades, and data backup. It is the primary reference for anyone setting up a new environment.
Deployment Topologies
Maestro supports two deployment models depending on how many stations you are running.
Single-station (all-in-one)
All services run on one machine. This is the quickest path for evaluation, development, and single-bench labs.
┌──────────────────────────────────────────────┐
│ One machine │
│ PostgreSQL · Redis · API · UI · Runners │
└──────────────────────────────────────────────┘
Multi-station (shared infrastructure)
One central server hosts the database and fleet dashboard. Each test station runs its own API, UI, and runners, and connects outbound to the shared database.
┌──────────────────────────────────────────────┐
│ CENTRAL SERVER │
│ PostgreSQL · Dashboard · pgAdmin │
└────────────────┬─────────────────────────────┘
│ (network, port 5432)
┌───────────┴───────────┐
│ │
┌────┴──────────┐ ┌─────────┴──────────┐
│ STATION A │ │ STATION B │
│ API · UI │ │ API · UI │
│ Runners │ │ Runners │
└───────────────┘ └────────────────────┘
|
Component |
Central |
Station |
|---|---|---|
|
PostgreSQL (results DB) |
✅ |
— |
|
Fleet Dashboard |
✅ |
— |
|
pgAdmin |
✅ |
— |
|
Maestro API |
— |
✅ |
|
Maestro UI |
— |
✅ |
|
.NET Runner |
— |
✅ |
|
Python Runner |
— |
✅ |
|
Redis |
— |
✅ |
Prerequisites
All machines
|
Requirement |
Version |
Notes |
|---|---|---|
|
Docker Engine + Compose V2 |
24+ |
|
|
Network access to central server |
— |
Port 5432 from stations to central |
|
GitLab Personal Access Token |
— |
|
Platform-specific Docker setup
|
Platform |
Installation method |
|---|---|
|
Windows 10/11 |
Install Docker Desktop; enable the WSL 2 backend |
|
Ubuntu / Debian |
|
|
Raspberry Pi (64-bit OS) |
Same as Ubuntu/Debian above |
Raspberry Pi: The 64-bit Raspberry Pi OS is required. The 32-bit (armhf) image is not supported because .NET 10 requires arm64.
Generating a GitLab Personal Access Token
A GitLab PAT is required to pull Maestro container images from the registry and to access test package repositories.
-
Log in to your GitLab instance
-
Go to User Settings → Access Tokens
-
Create a token with the following scopes:
read_registry,read_repository -
Copy the token — you will need it during installation
Installing Maestro
Step 1 — Install the Central Server
Run this once on the machine that will host the shared database and dashboard.
Option A — one-line installer (recommended)
curl -fsSL https://git.esharp.se/testdevelopment/tat/-/raw/main/deploy/install-central.sh | \
bash -s -- \
--postgres-password "YOUR_STRONG_PASSWORD" \
--pgadmin-password "YOUR_PGADMIN_PASSWORD" \
--git-token "glpat-xxxxxxxxxxxx"
Option B — manual setup
mkdir -p ~/tat-central && cd ~/tat-central
curl -fsSL \
-H "PRIVATE-TOKEN: glpat-xxxxxxxxxxxx" \
"https://git.esharp.se/testdevelopment/tat/-/raw/main/deploy/central/docker-compose.central.yml" \
-o docker-compose.yml
cat > .env <<EOF
POSTGRES_PASSWORD=YOUR_STRONG_PASSWORD
PGADMIN_EMAIL=admin@example.com
PGADMIN_PASSWORD=YOUR_PGADMIN_PASSWORD
EOF
echo "glpat-xxxxxxxxxxxx" | docker login git.esharp.se:5050 -u oauth2 --password-stdin
docker compose pull
docker compose up -d
Verify
docker compose ps
# postgres (healthy), dashboard (healthy), pgadmin (running)
|
Service |
URL |
|---|---|
|
Fleet Dashboard |
|
|
pgAdmin |
|
Note the IP address of this machine — you will need it when installing each station.
Step 2 — Install Each Test Station
Run this on every machine that will execute tests. For a single-station setup, run it on the same machine as Step 1.
Option A — one-line installer (recommended)
# Linux / macOS / WSL
curl -fsSL https://git.esharp.se/testdevelopment/tat/-/raw/main/deploy/install-station.sh | \
bash -s -- \
--postgres-host "192.168.1.100" \
--postgres-password "YOUR_STRONG_PASSWORD" \
--git-token "glpat-xxxxxxxxxxxx" \
--station-name "station-lab-01"
On Windows, run this in Git Bash or WSL:
curl -fsSL https://git.esharp.se/testdevelopment/tat/-/raw/main/deploy/install-station.sh | \
bash -s -- \
--postgres-host "192.168.1.100" \
--postgres-password "YOUR_STRONG_PASSWORD" \
--git-token "glpat-xxxxxxxxxxxx" \
--station-name "station-dev-alice"
Option B — manual setup
mkdir -p ~/tat-station && cd ~/tat-station
curl -fsSL \
-H "PRIVATE-TOKEN: glpat-xxxxxxxxxxxx" \
"https://git.esharp.se/testdevelopment/tat/-/raw/main/deploy/station/docker-compose.station.yml" \
-o docker-compose.yml
cat > .env <<EOF
POSTGRES_HOST=192.168.1.100
POSTGRES_PORT=5432
POSTGRES_PASSWORD=YOUR_STRONG_PASSWORD
GIT_HOST=git.esharp.se
STATION_NAME=station-lab-01
EOF
echo "glpat-xxxxxxxxxxxx" | docker login git.esharp.se:5050 -u oauth2 --password-stdin
docker compose pull
docker compose up -d
Verify
docker compose ps
# api (healthy), ui (healthy), dotnet-runner, python-runner, redis (healthy)
|
Service |
URL |
|---|---|
|
Maestro UI |
|
|
Maestro API |
|
|
API Swagger |
|
Step 3 — Enter Your Personal Access Token
The GitLab PAT used during installation authenticates Docker image pulls only. To allow Maestro itself to download and publish test packages, each station operator enters their own PAT through the UI.
-
Open
<http://localhost:7001/station-config> -
Scroll to the Package Registry section
-
Enter your GitLab Personal Access Token
-
Click Save
The token is stored in the database and is never written to any file or container environment variable.
Database schema
Maestro applies database migrations automatically on first API startup via EF Core. No manual schema setup or SQL scripts are required.
Initial Configuration Checklist
Complete these steps after installation before using Maestro in production.
|
# |
Item |
Where |
Notes |
|---|---|---|---|
|
1 |
Change default PostgreSQL password |
|
Never use the default in a production environment |
|
2 |
Change pgAdmin password |
|
Restrict pgAdmin access to admin browsers |
|
3 |
Set a unique station name |
|
Used to scope configuration and label all results |
|
4 |
Configure the package registry URL |
Station Config UI → Package Registry |
Point to your |
|
5 |
Enter GitLab PAT |
Station Config UI → Package Registry |
Required for package download and publish |
|
6 |
Set station-local config values |
Station Config UI |
COM ports, instrument addresses, fixture IDs |
|
7 |
Configure |
|
e.g. |
|
8 |
Enable HTTPS |
Reverse proxy (nginx / Caddy) or Kestrel TLS |
Required before exposing the UI outside the local network |
|
9 |
Set |
Docker environment |
Disables developer exception pages |
|
10 |
Pin image tags |
|
Use a commit SHA instead of |
|
11 |
Schedule database backups |
Cron / task scheduler |
Licensing & Access
Maestro is distributed as container images through a private GitLab registry. Access is controlled by your GitLab Personal Access Token — there is no separate license key or activation step.
If your token expires or is revoked, stations will be unable to pull updated images. Existing running containers are not affected until the next upgrade.
To generate or renew a token:
-
Log in to your GitLab instance
-
Go to User Settings → Access Tokens
-
Create or rotate a token with
read_registryandread_repositoryscopes -
Re-authenticate each Docker host:
echo "<token>" | docker login git.esharp.se:5050 -u oauth2 --password-stdin
Network, Firewall, and Security Requirements
Required open ports (inbound on the central server)
|
Port |
Service |
Allowed from |
|---|---|---|
|
5432 |
PostgreSQL |
All station IPs |
|
5002 |
Fleet Dashboard |
Operator browsers (optional) |
|
5088 |
pgAdmin |
Admin machines only |
Stations do not require any inbound ports. All connections are outbound — to the central server (port 5432) and to the GitLab container registry (port 443).
Firewall recommendations
-
Restrict PostgreSQL (5432) to known station IP addresses using firewall rules or a VPN
-
Do not expose pgAdmin (5088) to the open internet
-
Place Maestro API (7000) and UI (7001) behind a reverse proxy with TLS if accessed over untrusted networks
HTTPS setup
For production deployments, terminate TLS at a reverse proxy in front of the Maestro UI and API. A minimal nginx configuration:
server {
listen 443 ssl;
server_name maestro.example.com;
ssl_certificate /etc/ssl/certs/maestro.crt;
ssl_certificate_key /etc/ssl/private/maestro.key;
location / {
proxy_pass http://localhost:7001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
SignalR (used for real-time updates) requires WebSocket support. Ensure your proxy forwards
UpgradeandConnectionheaders as shown above.
Secrets management
|
Secret |
Where to store |
|---|---|
|
PostgreSQL password |
|
|
pgAdmin password |
|
|
GitLab PAT (for Docker) |
Used once during install; not persisted |
|
GitLab PAT (for packages) |
Stored in the Maestro database via the Station Config UI |
Upgrading Maestro
Maestro images are rebuilt on every push to main. Each image is tagged with both latest and the commit SHA.
Update a station
cd ~/tat-station
docker compose pull
docker compose up -d
This pulls the latest images for your architecture and restarts only containers where the image has changed. Redis data and any mounted volumes are preserved.
Update the central server
cd ~/tat-central
docker compose pull
docker compose up -d
The PostgreSQL data volume is untouched during upgrades. Only the Dashboard container is replaced.
Pin a specific version
To deploy a specific build rather than latest, set IMAGE_TAG in .env:
# ~/tat-station/.env or ~/tat-central/.env
IMAGE_TAG=abc1234
Then run docker compose pull && docker compose up -d.
Database migrations
EF Core migrations run automatically when the API container starts after an upgrade. Migrations are always forward-only and non-destructive. No manual intervention is required.
If a migration fails at startup (visible in
docker compose logs api), do not restart repeatedly. Restore from a backup, investigate the migration error, and contact support.
Backing Up and Restoring Maestro Data
All persistent Maestro state lives in PostgreSQL. Redis is ephemeral (variable bus only) and does not need to be backed up.
What to back up
|
Data |
Storage |
Backup method |
|---|---|---|
|
Test results, measurements, logs |
PostgreSQL volume |
|
|
Station configuration |
PostgreSQL volume |
Included in |
|
Package lifecycle overrides |
PostgreSQL volume |
Included in |
|
|
Host filesystem |
Copy to secure storage |
|
Test packages (YAML + code) |
Git repositories |
Maintained in GitLab |
Creating a database backup
# Dump the entire database to a compressed file
docker exec workflowengine-postgres \
pg_dump -U postgres testautomation \
| gzip > maestro-backup-$(date +%Y%m%d-%H%M%S).sql.gz
For scheduled backups, add this to cron (Linux) or Task Scheduler (Windows):
# /etc/cron.d/maestro-backup — runs daily at 02:00
0 2 * * * root docker exec workflowengine-postgres \
pg_dump -U postgres testautomation \
| gzip > /backups/maestro-$(date +\%Y\%m\%d).sql.gz
Restoring from a backup
# Stop the API to prevent writes during restore
docker compose stop api ui
# Restore
gunzip -c maestro-backup-20260101-020000.sql.gz | \
docker exec -i workflowengine-postgres \
psql -U postgres testautomation
# Restart services
docker compose start api ui
Volume snapshot (alternative)
If your infrastructure supports volume snapshots (e.g., LVM, ZFS, cloud block storage), snapshot the Docker volume directly:
# Find the volume
docker volume inspect workflowengine-postgres-data
# Take a filesystem-level snapshot via your platform tooling
Volume snapshots are faster than pg_dump for large databases but require the database to be quiesced or the PostgreSQL volume to support consistent snapshots.
Recovery time objectives
|
Scenario |
Recovery approach |
Estimated time |
|---|---|---|
|
Accidental data deletion |
Restore from |
Minutes |
|
Container or host failure |
Pull images + restore volume |
< 30 minutes |
|
Full station reinstall |
Run installer + restore DB |
< 1 hour |
Environment Variable Reference
Station .env
|
Variable |
Required |
Default |
Description |
|---|---|---|---|
|
|
✅ |
— |
Central PostgreSQL server IP or hostname |
|
|
— |
|
PostgreSQL port |
|
|
✅ |
— |
PostgreSQL password |
|
|
— |
|
GitLab hostname |
|
|
— |
machine hostname |
Unique station identifier |
|
|
— |
|
e.g. |
|
|
— |
|
Container image version to deploy |
Central server .env
|
Variable |
Required |
Default |
Description |
|---|---|---|---|
|
|
✅ |
— |
PostgreSQL superuser password |
|
|
— |
|
Exposed PostgreSQL port |
|
|
— |
|
pgAdmin login email |
|
|
✅ |
— |
pgAdmin login password |
|
|
— |
|
Dashboard timezone |
Troubleshooting
Station cannot connect to PostgreSQL
docker run --rm postgres:16-alpine \
pg_isready -h <central-ip> -p 5432 -U postgres
If this fails, verify that port 5432 is open on the central server's firewall and that POSTGRES_HOST in .env is correct.
Images fail to pull
# Re-authenticate
echo "glpat-xxx" | docker login git.esharp.se:5050 -u oauth2 --password-stdin
# Confirm the image exists
docker manifest inspect git.esharp.se:5050/testdevelopment/tat/api:latest
Raspberry Pi: "no matching manifest for linux/arm/v7"
You are running the 32-bit Raspberry Pi OS. Switch to the 64-bit version using Raspberry Pi Imager: Choose OS → Raspberry Pi OS (64-bit).
Check service logs
cd ~/tat-station
docker compose logs api # API startup and migration logs
docker compose logs dotnet-runner # .NET runner logs
docker compose logs -f # Follow all service logs