Flow Metrics Server
Type a query, see your flow metrics live in the browser.
One small container serves the web app and its API. You type (or pick) a Jira or Azure DevOps query, and the charts — cycle time, throughput, WIP, work item age, workflow board — appear immediately. Boards you save are kept on the server, so they are still there tomorrow.
Your tracker token stays on the server; the browser never sees it. The charts are computed in your browser, so your work-item data goes nowhere else.
Setup
You need Docker or Podman with Compose v2, and the access token from your license provider. If you do not have a container engine yet, see Install.
There are two commands in total. Pick whichever way suits you — they do the same thing.
Hand it to your assistant. Paste this into Claude Code, Cursor, or any assistant that can run commands here:
I have the flow-metrics-base repo checked out and an access token from my
license provider. From the flow-metrics-server directory:
1. Sign me in to registry.gitlab.com. The username is `token` and the
password is my access token — ask me to paste it when you need it, and
don't write it into a file or into a command you leave behind.
2. Start the server with Compose.
3. Run `docker compose ps` and tell me the exact URL to open — read the
real published port from the PORTS column, because the server's own
startup log always says 3301 even when it isn't.
Then tell me what the first screen will ask me for.
Use the menu — no command line at all. In the
flow-metrics-server folder:
- Windows — double-click
run_flow_metrics_server.cmd. - Linux / macOS — run
./run_flow_metrics_server.sh(or./run_flow_metrics_server.ps1if you have PowerShell).
A menu opens. Choose 7) Sign in to the image registry and paste your access token (once per machine), then 1) Start the server. It prints the address to open. The same menu later stops the server, updates it, and wipes the database.
Use a terminal. From the
flow-metrics-server directory:
# 1. Sign in to the image registry — once per machine.
# Username is 'token'; the password is your access token.
docker login registry.gitlab.com
# 2. Start the server.
docker compose up -dWhichever you chose, open http://localhost:3301 and follow the screens:
- Set a password. Put it in your password manager first — see Your password.
- Connect your tracker. Enter your Jira or Azure DevOps URL, your account and your API token; the wizard can test the connection before saving.
- Build a board. You land on your board list, empty at first. Choose New board, type a query, pick your workflow columns — status tiles drop exactly where you point them — and save; it gets its own link and stays in the list for next time. A collapsible flow graph above the board draws the moves your work items actually made between statuses, so you can check your column mapping against reality.
That is the whole setup — no files to create and nothing to configure. Everything you enter is kept on the server, so you only do it once.
Where do I get a tracker token?
- Jira Cloud — https://id.atlassian.com/manage-profile/security/api-tokens
- Jira Server / Data Center — your profile → Personal Access Tokens
- Azure DevOps — User settings → Personal access tokens, with the Work Items (Read) scope
To change your tracker connection later, use Settings in the app — you never have to re-enter the token to edit anything else. You can switch between Jira and Azure DevOps there too; if saved boards depend on custom fields the new tracker does not provide, the app warns you before the switch.
Your password
Save it in your password manager before you type it into the app.
Your tracker credentials are encrypted with this password inside the server’s data volume. That is what makes them worthless to anyone who copies the volume off this machine — and it has three consequences worth knowing up front:
You can change it, but you cannot recover it. While you are logged in, Settings → Change password takes the current one and sets a new one. A password you have forgotten is gone: nobody can recover it, including your license provider.
The way back in from a forgotten password is a wipe, run on this machine. It erases everything — the password, your tracker connection, and every saved board — and leaves you at first-run setup. Use the menu’s 8) Wipe the database, or:
docker compose down -v && docker compose up -dShell access to this machine is the trust boundary, which is why the login screen cannot offer this as a button. The screen names the underlying flag,
flow-metrics-server --wipe; with Compose the two commands above are the equivalent, and are what the menu runs.You log in again after every restart. Your session lives in the server’s memory, so restarting the container, rebooting the machine, or updating to a new version signs you out. That is expected, not a fault.
Everyday commands
The menu (run_flow_metrics_server.cmd on Windows,
./run_flow_metrics_server.sh on Linux/macOS) covers all of
these. If you prefer a terminal, run these from the
flow-metrics-server directory.
| What | Menu | Command |
|---|---|---|
| Start | 1 | docker compose up -d |
| Stop | 2 | docker compose down |
| See the logs | 4 | docker compose logs -f |
| Update to a new release | 6 | git pull && docker compose pull && docker compose up -d |
| Change tracker settings | in app | In the app → Settings |
| Change your password | in app | In the app → Settings → Change password |
| Erase everything | 8 | docker compose down -v && docker compose up -d |
Your password, tracker connection and saved boards survive stopping, restarting and updating — they live in a Docker volume, not in the container.
Good to know
The web app is at port 3301 by default. To use a
different one — say 3301 is already busy — create a .env
file next to docker-compose.yml containing
HOST_PORT=3309, then start again. Open the port you
chose.
Only this machine can reach it. The port is
published on localhost deliberately. Every part of the app
now requires your password, so this is no longer about stopping a
stranger from erasing your data — it is that the server speaks plain
HTTP, and a wider setting would send your password across the network
unencrypted. If your team needs access, ask your license provider about
putting a reverse proxy with HTTPS in front, rather than changing
this.
The startup message always says 3301, even when it isn’t. The server can only see its own port inside the container, not the one you open. If in doubt, this prints the real one:
docker compose psLook at the PORTS column:
127.0.0.1:3309->3301/tcp means open
3309.
Everything is in one Docker volume
(fmb-flow-metrics-data) — password, tracker connection,
saved boards, cache. docker compose down -v deletes it
without asking, and that cannot be undone; it is the same as clearing
the database. Plain docker compose down is safe.
Upgrading
The version is pinned in docker-compose.yml, so
git pull is what brings a new release — it
updates the pin, and docker compose up -d then starts it.
The menu’s 6) Update does both. Your password, tracker
connection and saved boards live in the Docker volume and are untouched
by an upgrade.
From 8.x. Nothing to do for most people. Three things to expect:
- You will be signed out and the first board is slower. Both are normal for any update — the sign-out because sessions live in memory, and the slower first load because version 9 refreshes the cached work items once.
- Settings now shows one field table instead of the separate “Additional fields”, “Epic Link field id” and “Story Points field id” boxes. It lists your tracker’s own field names next to the ids your boards use, so you pick from a list rather than remembering an id. Nothing to migrate — it is a new view of the same settings.
- Only if you had filled in “Epic Link field id” or “Story Points field id”: the app will tell you that setting moved. Open Settings, set the same field in the new table, and save. It is a one-time edit and your boards are untouched.
Also new: you can download your tracker connection as a
source.yaml from Settings — handy for running the
same connection in a scheduled job — and it never contains your token,
only a ${JIRA_TOKEN} placeholder. Connection problems now
say what actually failed (unreachable versus slow) instead of one
generic message.
From 7.0.x. Nothing to do. Two things moved, both covered above: the login screen’s “Clear my database” button is gone — a forgotten password is now recovered with a wipe on this machine — and changing a password you know is a dialog under Settings.
From before version 7. Version 7 added the password and moved tracker setup into the browser. After updating:
- Open the app and set a password. The API returns “unauthorized” until you do.
- Enter your tracker connection once in the wizard. Your saved boards are untouched.
- If this directory still has a
source.yamlfrom before, it is no longer used. Paste its contents into the wizard’s import box to fill the form in for you, then delete the file once you are done — it holds your token and it is the only copy here.
If something goes wrong
The page will not load. Check what port is actually
published with docker compose ps and open that one. If the
container is not listed, start it with
docker compose up -d.
“Unauthorized” when pulling the image, or
docker compose up cannot find it. You are not
signed in to the registry. Run
docker login registry.gitlab.com — username
token, password your access token — and try again.
It asks for my password again. Expected after any restart or update — see Your password.
I forgot my password. There is no way back in, and nobody can recover it. Wiping resets the server to a fresh install — see Your password for the command. You will set a new password and re-enter your tracker connection, and your saved boards are gone.
The login screen tells me to run
flow-metrics-server --wipe. That is the underlying
flag; with Compose the equivalent is the menu’s 8) Wipe the
database or
docker compose down -v && docker compose up -d.
“Unauthorized” or “401” from your tracker. The token is wrong, expired, or was cut short when pasted. Fix it in the app under Settings.
The container keeps restarting. Look at the reason:
docker compose logs --tail 20Anything else — send the output of
docker compose logs --tail 50 to your license provider. It
will not contain your token or your password.
Support
Contact your license provider for support, access tokens, and product inquiries.