Self-hosted C++20 BaaS: instant REST APIs, auth, real-time, file uploads, and an admin dashboard.
10K+
MantisBase is a self-hosted Backend-as-a-Service written in C++20. A single binary gives you instant REST APIs, JWT authentication, realtime SSE and WebSocket streams, file uploads, and a web admin dashboard — with no runtime dependencies.
docker run -d --name mantisbase -p 7070:8080 allankoech/mantisbase
| Service | URL |
|---|---|
| REST API | http://localhost:7070/api/v1/ |
| Admin dashboard | http://localhost:7070/mb |
| Health check | http://localhost:7070/api/v1/health |
The server listens on port 8080 inside the container. Map it to any host port you like.
A container restart discards everything unless you mount volumes. Four directories are relevant:
| Path | Contents |
|---|---|
/mb/data | SQLite database, logs, and uploaded files |
/mb/public | Static files served at the site root |
/mb/scripts | JavaScript extensions (entry point index.mb.js) |
/mb/migrations | JSON migration files |
docker run -d --name mantisbase -p 7070:8080 \
-e MB_JWT_SECRET=change-me \
-v $(pwd)/data:/mb/data \
-v $(pwd)/www:/mb/public \
-v $(pwd)/scripts:/mb/scripts \
-v $(pwd)/migrations:/mb/migrations \
allankoech/mantisbase
On first boot, open http://localhost:7070/mb and create the admin account through the dashboard. To create one from the CLI instead:
docker exec -it mantisbase /mb/mantisbase admins --add [email protected] 'your-strong-password'
Set MB_DEFAULT_ADMIN_EMAIL and MB_DEFAULT_ADMIN_PASSWORD to run admins --add without arguments, or set MB_SKIP_ADMIN_SETUP=1 to skip the first-boot setup prompt entirely.
services:
mantisbase:
image: allankoech/mantisbase:latest
restart: unless-stopped
ports:
- "7070:8080"
volumes:
- ./data:/mb/data
- ./www:/mb/public
- ./scripts:/mb/scripts
- ./migrations:/mb/migrations
environment:
MB_JWT_SECRET: ${MB_JWT_SECRET:-}
docker compose up -d
All environment variables use the MB_ prefix. When a variable is set, it overrides the matching CLI option.
| Variable | Description | Default |
|---|---|---|
MB_JWT_SECRET | JWT secret key for token signing — set this in production | built-in default |
MB_DATABASE_TYPE | Database backend: sqlite3 or postgresql | sqlite3 |
MB_DATABASE_URL | Database connection string (required for PostgreSQL) | none |
MB_LOG_LEVEL | Minimum log level: trace, debug, info, warn, critical | info |
MB_DISABLE_FILE_UPLOADS | Set to 1 to reject file uploads with 403 Forbidden | 0 |
MB_DISABLE_RATE_LIMIT | Set to 1 to disable rate limiting | rate limiting enabled |
MB_REALTIME_SSE | Set to 0 to disable the SSE realtime endpoint | 1 |
MB_REALTIME_WS | Set to 0 to disable the WebSocket realtime endpoint | 1 |
MB_OAUTH_ENCRYPTION_KEY | Encryption key for stored OAuth provider secrets | none |
MB_SKIP_ADMIN_SETUP | Set to 1 to skip the first-boot admin setup prompt | 0 |
MB_DISABLE_ADMIN_ON_FIRST_BOOT | Set to 1 to skip creating an admin on first boot | 0 |
MB_DEFAULT_ADMIN_EMAIL | Admin email used by admins --add with no arguments | none |
MB_DEFAULT_ADMIN_PASSWORD | Admin password used by admins --add with no arguments | none |
The image entrypoint is /mb/mantisbase and the default command is serve -p 80. Anything you append after the image name replaces that default command, so include serve -p 80 yourself:
# Verbose development logging
docker run -p 7070:80 allankoech/mantisbase --dev serve -p 80
Global options such as --db and --db_url must come before the serve subcommand:
docker run -d -p 7070:80 \
-e MB_JWT_SECRET=change-me \
allankoech/mantisbase \
--db postgresql \
--db_url "dbname=mantis host=postgres port=5432 user=postgres password=postgres" \
serve -p 80
See the CLI reference for every flag.
/api/v1/realtime and WebSocket at /api/v1/realtime/ws/api/v1/files/<entity>/<filename>| Tag | Description |
|---|---|
latest | Most recent stable release |
alpha | Most recent pre-release |
0.4.0-alpha.6, 0.3.7, 0.3.6, 0.3.5 | Specific versions |
Published images are linux/amd64 only. The Dockerfile also supports arm64 — build it yourself with docker build --platform linux/arm64.
The image is based on debian:stable-slim and is roughly 40 MB compressed.
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:80/api/v1/health || exit 1
livenessProbe:
httpGet:
path: /api/v1/health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
A healthy server returns 200 OK with {"status": "OK"}.
# List records
curl http://localhost:7070/api/v1/entities/posts
# Create a record
curl -X POST http://localhost:7070/api/v1/entities/posts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"title": "My Post", "content": "Hello World!"}'
# Upload a file with a record
curl -X POST http://localhost:7070/api/v1/entities/posts \
-H "Authorization: Bearer <token>" \
-F "title=My Post" \
-F "[email protected]"
MIT © 2025 Allan K. Koech
Content type
Image
Digest
sha256:a9ee7f201…
Size
44.8 MB
Last updated
1 day ago
docker pull allankoech/mantisbase