Skip to content

Plugin Reference

BRIDGEPORT uses a JSON-based plugin system to define service types (Django, Node.js, etc.) and database types (PostgreSQL, MySQL, etc.) with their commands, connection fields, and monitoring queries. This page covers the plugin format, directory structure, lifecycle, and how to create your own plugins.

Plugins are JSON files in the plugins/ directory (configurable via the PLUGINS_DIR environment variable, default: ./plugins). On every server startup, BRIDGEPORT’s syncPlugins() function reads these files and synchronizes them to the database.

Plugins define two things:

  1. Service types — Predefined shell commands for containers (e.g., python manage.py shell for Django)
  2. Database types — Connection field definitions, backup/restore command templates, shell commands, and monitoring queries

Admins can also create and edit types through the UI. The plugin system tracks whether a type has been customized via the UI, and uses smart merge logic to avoid overwriting manual edits.


plugins/
├── schemas/
│ ├── service-type.schema.json # JSON Schema for validation
│ └── database-type.schema.json # JSON Schema for validation
├── service-types/
│ ├── django.json
│ ├── fastapi.json
│ ├── flask.json
│ ├── nodejs.json
│ ├── caddy.json
│ ├── nginx.json
│ ├── celery.json
│ ├── keycloak.json
│ ├── redis.json
│ ├── postgres.json
│ ├── generic.json
│ └── your-custom-type.json # Add your own here
└── database-types/
├── postgres.json
├── mysql.json
├── sqlite.json
├── mongodb.json
├── redis.json
└── your-custom-db.json # Add your own here

File names must match the name field inside the JSON (e.g., django.json contains "name": "django").


Service types define predefined commands that can be run inside containers via the UI or the bridgeport run CLI command.

{
"name": "lowercase-with-hyphens",
"displayName": "Human Readable Name",
"commands": [
{
"name": "command-slug",
"displayName": "Command Label",
"command": "actual shell command",
"description": "What this command does",
"sortOrder": 0
}
]
}
FieldTypeRequiredDescription
namestringYesUnique identifier. Lowercase alphanumeric with hyphens (^[a-z0-9-]+$)
displayNamestringYesHuman-readable name shown in the UI
commandsarrayYesList of predefined commands

Command fields:

FieldTypeRequiredDescription
namestringYesCommand slug (^[a-z0-9-]+$). Used in the CLI: bridgeport run ... <name>
displayNamestringYesLabel shown in the UI
commandstringYesThe shell command to execute inside the container
descriptionstringNoBrief description of what the command does
sortOrderintegerNoDisplay order (lower numbers appear first, defaults to array index)
{
"name": "django",
"displayName": "Django",
"commands": [
{
"name": "shell",
"displayName": "Django Shell",
"command": "python manage.py shell",
"description": "Interactive Django shell",
"sortOrder": 0
},
{
"name": "dbshell",
"displayName": "Database Shell",
"command": "python manage.py dbshell",
"description": "Database CLI shell",
"sortOrder": 1
},
{
"name": "migrate",
"displayName": "Run Migrations",
"command": "python manage.py migrate",
"description": "Apply database migrations",
"sortOrder": 2
},
{
"name": "collectstatic",
"displayName": "Collect Static",
"command": "python manage.py collectstatic --noinput",
"description": "Collect static files",
"sortOrder": 4
}
]
}
  1. Create a new JSON file in plugins/service-types/:

    Terminal window
    touch plugins/service-types/rails.json
  2. Add the plugin definition:

    {
    "name": "rails",
    "displayName": "Ruby on Rails",
    "commands": [
    {
    "name": "console",
    "displayName": "Rails Console",
    "command": "bundle exec rails console",
    "description": "Interactive Rails console",
    "sortOrder": 0
    },
    {
    "name": "migrate",
    "displayName": "Run Migrations",
    "command": "bundle exec rails db:migrate",
    "description": "Apply database migrations",
    "sortOrder": 1
    },
    {
    "name": "routes",
    "displayName": "Show Routes",
    "command": "bundle exec rails routes",
    "description": "Display all routes",
    "sortOrder": 2
    }
    ]
    }
  3. Restart BRIDGEPORT. The new type will appear in the service type dropdown.


Database types define connection fields, backup/restore commands, shell commands, and optionally monitoring queries for collecting database metrics.

{
"name": "lowercase-with-hyphens",
"displayName": "Human Readable Name",
"defaultPort": 5432,
"connectionFields": [...],
"backupCommand": "...",
"restoreCommand": "...",
"commands": [...],
"monitoring": {
"connectionMode": "sql",
"driver": "pg",
"queries": [...]
}
}
FieldTypeRequiredDescription
namestringYesUnique identifier (^[a-z0-9-]+$)
displayNamestringYesHuman-readable name
defaultPortintegerNoDefault connection port
connectionFieldsarrayYesFields shown when adding a database of this type
backupCommandstringNoCommand template for backups (uses {{placeholders}})
restoreCommandstringNoCommand template for restoring backups
commandsarrayNoPredefined shell commands (same format as service type commands)
monitoringobjectNoMonitoring configuration for collecting metrics

Connection fields define the form shown when adding a database of this type.

FieldTypeRequiredDescription
namestringYesField identifier (used in command placeholders, e.g., host)
labelstringYesDisplay label (e.g., "Host")
typestringYesInput type: "text", "number", or "password"
requiredbooleanNoWhether the field is required
defaultanyNoDefault value

Backup commands, restore commands, and shell commands support {{placeholder}} template syntax. Placeholders are replaced with actual values at execution time.

Available placeholders:

PlaceholderSource
{{host}}Database host
{{port}}Database port
{{username}}Database username
{{password}}Database password
{{databaseName}}Database name
{{filePath}}File path (for SQLite)
{{outputFile}}Backup output file path (generated by BRIDGEPORT)
{{inputFile}}Restore input file path

Example backup command:

pg_dump --no-password -h {{host}} -p {{port}} -U {{username}} -d {{databaseName}} -f "{{outputFile}}"

The monitoring section defines how BRIDGEPORT collects metrics from databases of this type.

Monitoring configuration:

FieldTypeRequiredDescription
connectionModestringYes"sql" for direct database connections, "ssh" for command execution via SSH, "redis" for Redis INFO commands
driverstringConditionalNode.js driver for SQL connections: "pg" or "mysql2". Required when connectionMode is "sql"
queriesarrayYesList of monitoring queries

Query definition:

FieldTypeRequiredDescription
namestringYesUnique query identifier (used as the key in stored metrics)
displayNamestringYesLabel shown in the monitoring UI
querystringYesThe SQL query, shell command, or Redis command to execute
resultTypestringYesHow to interpret results: "scalar", "row", or "rows"
unitstringNoDisplay unit (e.g., "bytes", "%", "s")
chartGroupstringNoGroup queries together on the same chart
resultMappingobjectNoMaps column names to result field names (for rows type)

Result types explained:

TypeDescriptionQuery example
scalarReturns a single value from a column named valueSELECT count(*) AS value FROM users
rowReturns a single row with named columnsSELECT version() AS version, current_database() AS database
rowsReturns multiple rows (tables, top-N queries)SELECT name, size FROM tables ORDER BY size DESC LIMIT 10

For scalar queries, BRIDGEPORT reads the value column from the first row. For rows queries, use resultMapping to define which columns map to which display fields.

{
"name": "postgres",
"displayName": "PostgreSQL",
"defaultPort": 5432,
"connectionFields": [
{ "name": "host", "label": "Host", "type": "text", "required": true, "default": "localhost" },
{ "name": "port", "label": "Port", "type": "number", "required": true, "default": 5432 },
{ "name": "databaseName", "label": "Database Name", "type": "text", "required": true },
{ "name": "username", "label": "Username", "type": "text", "required": true },
{ "name": "password", "label": "Password", "type": "password", "required": true }
],
"backupCommand": "pg_dump --no-password -h {{host}} -p {{port}} -U {{username}} -d {{databaseName}} -f \"{{outputFile}}\"",
"restoreCommand": "pg_restore --no-password -h {{host}} -p {{port}} -U {{username}} -d {{databaseName}} \"{{inputFile}}\"",
"commands": [
{
"name": "shell",
"displayName": "PostgreSQL Shell",
"command": "psql -h {{host}} -p {{port}} -U {{username}} {{databaseName}}",
"description": "Interactive PostgreSQL shell",
"sortOrder": 0
},
{
"name": "vacuum",
"displayName": "Vacuum Analyze",
"command": "psql -h {{host}} -p {{port}} -U {{username}} -d {{databaseName}} -c \"VACUUM ANALYZE\"",
"description": "Run VACUUM ANALYZE",
"sortOrder": 1
}
],
"monitoring": {
"connectionMode": "sql",
"driver": "pg",
"queries": [
{
"name": "dbSize",
"displayName": "Database Size",
"query": "SELECT pg_database_size(current_database()) AS value",
"resultType": "scalar",
"unit": "bytes"
},
{
"name": "tableCount",
"displayName": "Table Count",
"query": "SELECT count(*) AS value FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE'",
"resultType": "scalar"
},
{
"name": "deadTupleRatio",
"displayName": "Dead Tuple Ratio",
"query": "SELECT CASE WHEN ... END AS value FROM pg_stat_user_tables",
"resultType": "scalar",
"unit": "%"
},
{
"name": "topTableSizes",
"displayName": "Top Tables by Size",
"query": "SELECT schemaname || '.' || relname AS name, pg_total_relation_size(relid) AS size, n_live_tup AS rows FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10",
"resultType": "rows",
"resultMapping": { "name": "name", "size": "size", "rows": "rows" }
}
]
}
}

Here is an example creating a monitoring-enabled ClickHouse plugin:

  1. Create plugins/database-types/clickhouse.json:

    {
    "name": "clickhouse",
    "displayName": "ClickHouse",
    "defaultPort": 8123,
    "connectionFields": [
    { "name": "host", "label": "Host", "type": "text", "required": true, "default": "localhost" },
    { "name": "port", "label": "Port", "type": "number", "required": true, "default": 8123 },
    { "name": "databaseName", "label": "Database", "type": "text", "required": true, "default": "default" },
    { "name": "username", "label": "Username", "type": "text", "default": "default" },
    { "name": "password", "label": "Password", "type": "password" }
    ],
    "commands": [
    {
    "name": "client",
    "displayName": "ClickHouse Client",
    "command": "clickhouse-client --host {{host}} --port 9000 --user {{username}} --database {{databaseName}}",
    "description": "Interactive ClickHouse client",
    "sortOrder": 0
    }
    ],
    "monitoring": {
    "connectionMode": "ssh",
    "queries": [
    {
    "name": "dbSize",
    "displayName": "Database Size",
    "query": "clickhouse-client --host {{host}} --port 9000 -q \"SELECT sum(bytes_on_disk) FROM system.parts WHERE database = '{{databaseName}}'\"",
    "resultType": "scalar",
    "unit": "bytes"
    },
    {
    "name": "tableCount",
    "displayName": "Table Count",
    "query": "clickhouse-client --host {{host}} --port 9000 -q \"SELECT count() FROM system.tables WHERE database = '{{databaseName}}'\"",
    "resultType": "scalar"
    }
    ]
    }
    }
  2. Restart BRIDGEPORT. ClickHouse will appear in the database type dropdown.


On every server startup, syncPlugins() reads all JSON files from plugins/service-types/ and plugins/database-types/, validates them, and synchronizes them to the database.

The sync result is logged to the console:

[Plugins] Syncing plugins from ./plugins
[Plugins] Sync complete: 2 created, 3 updated, 0 errors

When syncing plugins, BRIDGEPORT handles three cases:

ScenarioBehavior
Type does not existCreated from the JSON file. Source set to "plugin".
Type exists and is NOT customizedFully replaced with the JSON file contents (commands are deleted and recreated).
Type exists and IS customizedOnly new commands (by name) are added. Existing commands and display name are preserved.

This means you can safely update plugin JSON files and restart BRIDGEPORT — your admin’s UI customizations will not be overwritten. However, any new commands added to the JSON file will be picked up automatically.

Each service type and database type has an isCustomized boolean flag:

  • false (default for plugin-sourced types): The type can be fully overwritten by plugin sync
  • true (set when an admin edits via the UI): Plugin sync will only add new commands, not replace existing ones

The source field tracks origin:

  • "plugin": Created from a JSON file
  • "user": Created manually through the admin UI

If a type has been customized through the UI, you can reset it to the original plugin definition:

  1. Go to Admin > Service Types or Admin > Database Types
  2. Click the Reset button on a customized type

This reads the original JSON file, replaces all commands, and sets isCustomized back to false.

Any type (plugin-sourced or user-created) can be exported as a JSON file to the plugins directory:

  1. Go to Admin > Service Types or Admin > Database Types
  2. Click the Export button

This writes the current state (including any UI customizations) as a JSON file to the plugins directory. The exported file will be picked up by future plugin syncs.


BRIDGEPORT ships with these plugins:

Service Types:

NameDisplay NameCommands
djangoDjangoshell, dbshell, migrate, makemigrations, collectstatic, createsuperuser
fastapiFastAPIshell, pip-list, migrate, makemigration
flaskFlaskshell, routes, db-upgrade, repl
nodejsNode.jsrepl, npm-install, npm-build, npm-test
caddyCaddyversion, validate, reload, fmt, list-modules
nginxNginxtest-config, reload, version
celeryCelerystatus, inspect-active, inspect-scheduled, inspect-stats
keycloakKeycloakversion, show-config
redisRediscli, ping, info, dbsize
postgresPostgreSQLpsql, list-dbs, version
genericGenericsh, bash

Database Types:

NameDisplay NamePortMonitoringConnection Mode
postgresPostgreSQL5432YesSQL (pg driver)
mysqlMySQL3306YesSQL (mysql2 driver)
sqliteSQLiteYesSSH
mongodbMongoDB27017No
redisRedis6379YesRedis