Skip to content

Troubleshooting

A practical guide to diagnosing and fixing common BRIDGEPORT issues, with a quick-reference table and step-by-step debugging instructions.


BRIDGEPORT logs structured JSON in production and pretty-printed output in development. Start here for any issue:

Terminal window
# View recent logs
docker logs bridgeport --tail 100
# Follow logs in real time
docker logs bridgeport -f
# Search for errors
docker logs bridgeport 2>&1 | grep -i error

The /health endpoint gives you a quick status check:

Terminal window
curl -s http://localhost:3000/health | jq .
{
"status": "ok",
"timestamp": "2026-02-25T12:00:00.000Z",
"version": "1.0.0",
"bundledAgentVersion": "20260220-abc1234",
"cliVersion": "20260218-def5678"
}

If this endpoint does not respond, BRIDGEPORT is not running or not reachable.

Log MessageMeaning
=== BRIDGEPORT Startup ===Entrypoint script began
Applying migrations...Prisma is running pending database migrations
Prisma Migrate applied all migrations.Migrations completed successfully
=== Starting BRIDGEPORT ===Application is about to start
BRIDGEPORT running at http://0.0.0.0:3000Startup completed, ready to accept requests
[Scheduler] Starting with intervals:Background scheduler started
[Scheduler] Health check failed for server XSSH or URL health check failed for a server
[Scheduler] Auto-deploying container imageAuto-update triggered a deployment
Transient database contention; returning 503A write hit SQLite write-lock contention that outlasted the retry budget; the request got a retryable 503. Rare — see “Transient 503s under load” below.
Crypto not initializedMASTER_KEY is missing or invalid

Querying a Live Instance (Read-Only API Access)

Section titled “Querying a Live Instance (Read-Only API Access)”

When you (or a coding agent like Claude Code) are debugging an issue, it’s often faster to read the actual state of a running instance than to reason from the code alone — “which services exist? what tag is deployed? is this server marked unhealthy?”. BRIDGEPORT’s API answers all of these, and a read-only service account token lets you query it safely without touching anything.

1. Create a read-only token

In the UI: Admin → Service Accounts → New, give it the viewer role, then mint a token. A viewer token is read-only — every mutating call is rejected.

2. Put the URL + token in your .env (see .env.example):

Terminal window
BRIDGEPORT_URL="https://deploy.example.com"
BRIDGEPORT_TOKEN="bport_pat_..."

These two variables are not read by the BRIDGEPORT server — they’re purely for local tooling. .env is gitignored, so the token never gets committed. Treat it like any secret and revoke it from the Service Accounts page when done.

3. Query the API (everything authenticates with Authorization: Bearer):

Terminal window
# Load the vars (from the repo root)
set -a; . ./.env; set +a
# Who am I / what can this token do?
curl -s -H "Authorization: Bearer $BRIDGEPORT_TOKEN" "$BRIDGEPORT_URL/api/auth/me" | jq '{role, scopes}'
# List environments, then services in one of them
curl -s -H "Authorization: Bearer $BRIDGEPORT_TOKEN" "$BRIDGEPORT_URL/api/environments" | jq '.environments[] | {id, name}'
curl -s -H "Authorization: Bearer $BRIDGEPORT_TOKEN" "$BRIDGEPORT_URL/api/environments/<envId>/services" | jq '.services[] | {name, imageTag, healthStatus, serviceTypeId}'

The full surface is documented in the API Reference and the live OpenAPI spec at GET /openapi.json (Swagger UI at /api/docs). A viewer token is rejected (403, code: FORBIDDEN_ROLE) on any mutating call, so it’s safe to hand to automated tooling.

A token is a bearer credential — anyone holding it has its scopes. Never paste it into a file that gets committed, a chat log, or a shared terminal recording. Prefer short-lived tokens and rotate them after a debugging session.


SymptomLikely CauseSolution
Container exits on startupMissing MASTER_KEY or JWT_SECRETCheck .env has all required variables
Container exits with migration errorBad migration SQL in new releaseRestore backup, pin previous version, report bug
Can’t log in (no users)ADMIN_EMAIL/ADMIN_PASSWORD not set on first bootSet env vars, delete database, restart
Can’t connect to serverSSH key issue or firewallCheck key in Settings > SSH, test from Monitoring > Agents
Container not found during discoveryService not running or name changedCheck docker ps on the server, run manual discovery
Health check always failsWrong health endpoint URLVerify the URL in service settings, test with curl
Agent not reporting metricsToken mismatch or network issueRegenerate token, verify BRIDGEPORT_SERVER URL
Notifications not being sentSMTP/Slack not configuredCheck Admin > Notifications for channel configuration
Deploy fails with “image not found”Registry credentials expired or wrongRe-enter credentials in Registries page
Backup fails with timeoutLarge database exceeds default timeoutIncrease pgDumpTimeoutMs in System Settings
UI shows old versionBrowser cacheHard refresh (Ctrl+Shift+R or Cmd+Shift+R)
Metrics page is slowLarge metrics historyReduce retention in environment monitoring settings
MCP client gets 404 on /mcpMCP server disabledSet MCP_ENABLED=true and restart the container
MCP client sees no/few toolsToken role or env scope too narrowUse an operator/all-environments token; call get_capabilities
Occasional 503 SERVICE_UNAVAILABLE under heavy loadSQLite write-lock contention outlasted the retry budgetRetry the request (the response sets Retry-After); see below

BRIDGEPORT serves every request through a single SQLite connection, so under normal load DB work is naturally serialized and never self-contends. Contention can still arise when another writer holds the SQLite write lock — a long-running external transaction, a WAL checkpoint, or a second process touching the database file (for example a test harness that resets state between runs). Two cases result: a SQLITE_BUSY (the lock is held past busy_timeout) or a SQLITE_BUSY_SNAPSHOT (a stale read snapshot tried to upgrade to a write — busy_timeout can’t help with this one).

Both are transient and retryable. BRIDGEPORT retries the operation automatically with jittered backoff (DB_RETRY_* settings); if the contention outlasts the retry budget the request returns 503 with code: SERVICE_UNAVAILABLE and a Retry-After header — never an opaque 500. A well-behaved client (including the Terraform provider) should retry. Persistent 503s point at a genuine second writer on the DB file — remove it, or raise DB_RETRY_MAX_ATTEMPTS / SQLITE_BUSY_TIMEOUT_MS.

BRIDGEPORT requires three environment variables to start. If any are missing, it exits immediately:

Terminal window
# Check your .env file has these
DATABASE_URL=file:/data/bridgeport.db
MASTER_KEY=<your-32-byte-base64-key>
JWT_SECRET=<your-32-byte-base64-key>

Generate new keys if needed:

Terminal window
openssl rand -base64 32 # For MASTER_KEY
openssl rand -base64 32 # For JWT_SECRET (use a different value)

If the data directory is not writable, BRIDGEPORT cannot create or modify the database:

Terminal window
# Check ownership (should be writable by UID 1000, the node user)
ls -la ./data/
# Fix permissions if needed
sudo chown -R 1000:1000 ./data/

If the logs show a Prisma migration error during startup:

  1. Check the exact error in docker logs bridgeport
  2. This usually indicates a bug in a new release’s migration
  3. Restore your pre-upgrade backup and pin the previous version
  4. See Database Migration Issues for details

The initial admin user is only created when:

  1. ADMIN_EMAIL and ADMIN_PASSWORD are set in the environment
  2. No users exist in the database yet

If you started BRIDGEPORT without these variables, the database was created with no users.

Fix: Stop BRIDGEPORT, delete the database, set the variables, and restart:

Terminal window
docker compose stop
rm ./data/bridgeport.db
# Make sure ADMIN_EMAIL and ADMIN_PASSWORD are in .env
docker compose up -d

If an admin account exists but you forgot the password:

  • Another admin can reset it from the Admin > Users page
  • If no other admin exists, you will need to reset the database or update the password hash directly in the SQLite database

JWT tokens expire after 7 days. The UI handles token refresh automatically. If using API tokens for scripts, create a long-lived API token from the My Account modal instead.


  1. Verify the SSH key is uploaded in Settings > SSH for the correct environment

  2. Check the SSH user in Settings > General matches the authorized user on the server

  3. Test connectivity from Monitoring > Agents using the SSH test feature

  4. Try manual SSH from the BRIDGEPORT container:

    Terminal window
    docker exec -it bridgeport sh
    ssh -i /tmp/test-key user@server-ip -o StrictHostKeyChecking=no
IssueFix
”Connection refused”Ensure sshd is running on port 22 and firewall allows connections from BRIDGEPORT’s network
”Permission denied”Verify the public key is in ~/.ssh/authorized_keys on the target server
”Host key verification failed”BRIDGEPORT uses StrictHostKeyChecking=no by default; this error is rare but check SSH config
”Key format not supported”BRIDGEPORT expects OpenSSH format keys. Convert with ssh-keygen -p -m PEM -f key.pem
”Timeout”Network routing issue. Verify the server hostname/IP is reachable from the Docker network

If using socket mode for managing containers on the host:

Verify the Docker socket is mounted in docker-compose.yml:

volumes:
- /var/run/docker.sock:/var/run/docker.sock

Find the Docker socket group ID and add it to the container:

Terminal window
stat -c '%g' /var/run/docker.sock
# Output: 999 (or similar)

Add to your compose file:

services:
bridgeport:
group_add:
- "999" # Use the actual group ID from stat

On the target server:

Terminal window
# Check if the agent service is running
systemctl status bridgeport-agent
# View agent logs
journalctl -u bridgeport-agent -f --no-pager -n 50

From the agent server, check that BRIDGEPORT is reachable:

Terminal window
curl http://your-bridgeport-host:3000/health

If using an internal IP, ensure agentCallbackUrl is set correctly in Admin > System Settings.

If the agent logs show authentication errors:

  1. Go to the server’s detail page in BRIDGEPORT
  2. Regenerate the agent token
  3. Redeploy the agent (this updates the token automatically)

The agent needs access to the Docker socket to collect container metrics:

Terminal window
ls -la /var/run/docker.sock
# Add the agent user to the docker group if needed:
sudo usermod -aG docker $(whoami)

If a deploy fails with “image not found” or “pull access denied”:

  1. Verify the image name and tag exist in your registry
  2. Check that registry credentials are valid in the Registries page
  3. Test manually from the target server: docker pull your-image:tag

If deployment orchestration fails at the health check step:

  1. Check the deployment plan detail page for the specific error
  2. Verify the service’s healthCheckUrl returns a 2xx status
  3. Increase healthWaitMs if the service takes time to start
  4. Increase healthRetries for services with slow startup

If auto-rollback activated during an orchestrated deployment:

  1. Check the deployment plan detail page — it shows which step failed
  2. The error and logs fields on the failed step contain the root cause
  3. All previously deployed services were automatically rolled back to their previous tags

SymptomFix
”Connection refused”Verify database host/port are reachable from BRIDGEPORT’s server
”Authentication failed”Re-enter database credentials on the database detail page
”pg_dump: command not found”The BRIDGEPORT Docker image includes postgresql16-client. If using a different version, ensure compatibility
TimeoutIncrease pgDumpTimeoutMs in Admin > System Settings (default: 300000ms / 5 minutes)
  1. Verify storage credentials in Admin > Storage
  2. Ensure the target bucket exists
  3. Check that the access key has write permissions to the bucket
  1. Confirm the scheduler is enabled: SCHEDULER_ENABLED=true (default)
  2. Check the backup schedule is enabled on the database detail page
  3. Verify nextRunAt is set — if it shows null, the schedule may need to be re-saved

  1. Check that the notification type is enabled in Admin > Notifications
  2. Verify the user’s notification preferences include in_app for that type
  3. Check the notification bell icon — you may need to refresh
  1. Verify SMTP is configured in Admin > Notifications > SMTP
  2. Test the SMTP connection from the admin page
  3. Check BRIDGEPORT logs for SMTP errors: docker logs bridgeport 2>&1 | grep -i smtp
  4. Verify the user has email enabled in their notification preferences
  1. Verify Slack channels are configured in Admin > Notifications > Slack
  2. Check that notification types are routed to the correct Slack channel
  3. Test the webhook URL from the admin page

  1. Reduce metrics collection frequency: Raise the SCHEDULER_* interval env vars (e.g., change SCHEDULER_METRICS_INTERVAL from 60s to 300s — see Configuration Reference → Scheduler)
  2. Reduce retention: Lower METRICS_RETENTION_DAYS (env var) to reduce database size
  3. Check database size:
    Terminal window
    ls -lh ./data/bridgeport.db
  4. Vacuum the database (requires stopping BRIDGEPORT):
    Terminal window
    docker compose stop
    sqlite3 ./data/bridgeport.db "VACUUM;"
    docker compose start
  • Monitoring pages: Reduce the selected time range or the number of monitored resources
  • Health check logs: Reduce healthLogRetentionDays in Admin > System Settings > Retention
  • Audit logs: Reduce auditLogRetentionDays in System Settings

BRIDGEPORT runs prisma migrate deploy automatically on every startup. If a migration fails:

  1. Check the error message in docker logs bridgeport
  2. Do not modify the database manually — this breaks Prisma’s migration state
  3. Restore your pre-upgrade backup if the migration corrupted data
  4. Pin the previous version until the issue is fixed upstream

For legacy databases (created before migration tracking was added), BRIDGEPORT automatically creates a migration baseline on first startup. This is a one-time operation and should not require intervention.


The MCP server is opt-in and off by default. Common issues when connecting an AI agent:

The route is only registered when MCP is enabled. Verify:

  1. MCP_ENABLED is set to true or 1 — it is strict-parsed, so false, 0, an empty string, or any other value keeps it disabled.
  2. The container was restarted after setting it (it’s a deployment-level env var, not a runtime toggle).
  3. Check the boot logs: docker logs bridgeport 2>&1 | grep -i mcp.

You can confirm status without a client at Admin > MCP Server (/admin/mcp), which shows the live enabled/disabled badge and the endpoint URL.

Client connects but sees no tools (or fewer than expected)

Section titled “Client connects but sees no tools (or fewer than expected)”

The tools advertised to a session are derived from the token’s role and environment scope:

  • A viewer token sees read tools only. Write tools (deploy_service, restart_deployment, …) require an operator/admin token (services:write).
  • An environment-scoped token sees only the tools backed by environment routes — every global-route tool (all write tools, plus global reads like get_server) is hidden because it would always return FORBIDDEN_SCOPE. For the full surface, use an all-environments token.
  • Call the get_capabilities tool to see the exact scopes and tool list your token resolved to.

Client can’t reach /mcp through a reverse proxy

Section titled “Client can’t reach /mcp through a reverse proxy”

If the transport rejects the request, check MCP_ALLOWED_HOSTS: when set, the request Host header must match one of the listed public hostnames (DNS-rebinding protection). It is distinct from HOST (the socket bind address). Ensure TLS terminates at the proxy and the bearer token travels only over HTTPS.

See the MCP Server Reference for client setup and the full tool/scope reference.


If you lose your MASTER_KEY, all encrypted data becomes irrecoverable:

  • All secrets
  • SSH keys for every environment
  • Registry credentials
  • SMTP passwords
  • Slack webhook URLs
  • Spaces secret keys

What still works: Servers, services, environments, config files, users, metrics, audit logs, deployment history, and all other unencrypted data.

Recovery steps:

  1. Generate a new key: openssl rand -base64 32
  2. Update .env with the new MASTER_KEY
  3. Restart BRIDGEPORT
  4. Re-create all encrypted resources (SSH keys, secrets, registry credentials, SMTP config)

See Backup & Restore > Lost MASTER_KEY for detailed steps.