Self-Hosting Budibase: Low-Code Platform for Internal Tools
Every company has That Spreadsheet. The one that started as a quick tracker and evolved into a mission-critical monster with 47 tabs, three people who understand the formulas, and a prayer that nobody accidentally deletes row 4.
Budibase turns those spreadsheets into actual applications — with forms, tables, automations, and role-based access — without requiring you to be a full-stack developer. It’s an open-source low-code platform that lets you build internal tools on top of your existing data sources (PostgreSQL, MySQL, REST APIs, Google Sheets, or its own built-in database).
The managed Budibase cloud plan works fine for small teams, but self-hosting removes user limits, keeps your data on your infrastructure, and eliminates the recurring subscription. If you’re building tools that touch sensitive business data, self-hosting is the obvious choice.
Budibase vs Other Low-Code Platforms
| Feature | Budibase | Retool | Appsmith | NocoDB | Baserow |
|---|---|---|---|---|---|
| Open source | ✅ GPLv3 | ❌ Proprietary | ✅ Apache 2.0 | ✅ AGPLv3 | ✅ MIT |
| Built-in database | ✅ BudibaseDB | ❌ External only | ❌ External only | ✅ (is the DB) | ✅ (is the DB) |
| External data sources | ✅ SQL, REST, S3 | ✅ Extensive | ✅ SQL, REST, GraphQL | ⚠️ Limited | ⚠️ Limited |
| Automations | ✅ Built-in | ✅ Built-in | ⚠️ Basic | ✅ Webhooks | ✅ Webhooks |
| Form builder | ✅ Drag-and-drop | ✅ Drag-and-drop | ✅ Drag-and-drop | ⚠️ Basic | ⚠️ Basic |
| RBAC | ✅ App/table level | ✅ Granular | ✅ App level | ✅ View level | ✅ Table level |
| Self-host complexity | Medium | Hard | Medium | Easy | Easy |
| Resource usage | ~1.5GB RAM | N/A (cloud) | ~1GB RAM | ~300MB RAM | ~500MB RAM |
| AI features | ✅ Built-in (LiteLLM) | ✅ | ⚠️ Beta | ❌ | ❌ |
Budibase sits in a sweet spot: more capable than spreadsheet replacements like NocoDB or Baserow, but simpler and self-hostable unlike Retool. If you need custom internal apps (not just fancy tables), Budibase delivers.
Prerequisites
- A Linux server with Docker and Docker Compose installed (Ubuntu setup guide)
- A domain name pointed at your server’s public IP
- A reverse proxy (Caddy or Nginx) for HTTPS
- At least 2GB RAM (Budibase runs multiple services: app server, worker, CouchDB, Redis, MinIO, and a proxy)
- Basic familiarity with environment variables and Docker Compose
Docker Compose Setup
Budibase is a multi-service platform. The official stack includes an app server, background worker, CouchDB (database), Redis (caching), MinIO (object storage), and an Nginx-based proxy. Here’s a production-ready configuration:
First, create your project directory:
mkdir -p ~/budibase && cd ~/budibase
Create the environment file with secure credentials:
# .env
# Core secrets — generate unique values for each
API_ENCRYPTION_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)
INTERNAL_API_KEY=$(openssl rand -hex 32)
# CouchDB credentials
COUCH_DB_USER=budibase
COUCH_DB_PASSWORD=$(openssl rand -hex 16)
# MinIO credentials
MINIO_ACCESS_KEY=$(openssl rand -hex 12)
MINIO_SECRET_KEY=$(openssl rand -hex 24)
# Redis
REDIS_PASSWORD=$(openssl rand -hex 16)
# Main port — the proxy listens here
MAIN_PORT=10000
# Environment
BUDIBASE_ENVIRONMENT=PRODUCTION
Generate the actual .env file:
cat > .env << 'ENVFILE'
API_ENCRYPTION_KEY=
JWT_SECRET=
INTERNAL_API_KEY=
COUCH_DB_USER=budibase
COUCH_DB_PASSWORD=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
REDIS_PASSWORD=
MAIN_PORT=10000
BUDIBASE_ENVIRONMENT=PRODUCTION
ENVFILE
# Fill in random secrets
sed -i "s/^API_ENCRYPTION_KEY=.*/API_ENCRYPTION_KEY=$(openssl rand -hex 32)/" .env
sed -i "s/^JWT_SECRET=.*/JWT_SECRET=$(openssl rand -hex 32)/" .env
sed -i "s/^INTERNAL_API_KEY=.*/INTERNAL_API_KEY=$(openssl rand -hex 32)/" .env
sed -i "s/^COUCH_DB_PASSWORD=.*/COUCH_DB_PASSWORD=$(openssl rand -hex 16)/" .env
sed -i "s/^MINIO_ACCESS_KEY=.*/MINIO_ACCESS_KEY=$(openssl rand -hex 12)/" .env
sed -i "s/^MINIO_SECRET_KEY=.*/MINIO_SECRET_KEY=$(openssl rand -hex 24)/" .env
sed -i "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$(openssl rand -hex 16)/" .env
Now the Docker Compose file:
# docker-compose.yml
services:
app-service:
restart: unless-stopped
image: budibase/apps
container_name: bbapps
environment:
SELF_HOSTED: 1
COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
WORKER_URL: http://worker-service:4003
MINIO_URL: http://minio-service:9000
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
INTERNAL_API_KEY: ${INTERNAL_API_KEY}
BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT}
PORT: 4002
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: info
ENABLE_ANALYTICS: "false"
REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
depends_on:
- worker-service
- redis-service
worker-service:
restart: unless-stopped
image: budibase/worker
container_name: bbworker
environment:
SELF_HOSTED: 1
PORT: 4003
CLUSTER_PORT: ${MAIN_PORT}
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
MINIO_URL: http://minio-service:9000
APPS_URL: http://app-service:4002
COUCH_DB_USERNAME: ${COUCH_DB_USER}
COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD}
COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
INTERNAL_API_KEY: ${INTERNAL_API_KEY}
REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
depends_on:
- redis-service
- minio-service
minio-service:
restart: unless-stopped
image: minio/minio
volumes:
- minio_data:/data
environment:
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
MINIO_BROWSER: "off"
command: server /data --console-address ":9001"
healthcheck:
test: "timeout 5s bash -c ':> /dev/tcp/127.0.0.1/9000' || exit 1"
interval: 30s
timeout: 20s
retries: 3
proxy-service:
restart: unless-stopped
ports:
- "${MAIN_PORT}:10000"
container_name: bbproxy
image: budibase/proxy
environment:
- PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10
- PROXY_RATE_LIMIT_API_PER_SECOND=50
- APPS_UPSTREAM_URL=http://app-service:4002
- WORKER_UPSTREAM_URL=http://worker-service:4003
- MINIO_UPSTREAM_URL=http://minio-service:9000
- COUCHDB_UPSTREAM_URL=http://couchdb-service:5984
- RESOLVER=127.0.0.11
depends_on:
- minio-service
- worker-service
- couchdb-service
couchdb-service:
restart: unless-stopped
image: budibase/couchdb
environment:
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
- COUCHDB_USER=${COUCH_DB_USER}
volumes:
- couchdb_data:/opt/couchdb/data
redis-service:
restart: unless-stopped
image: redis:7-alpine
command: redis-server --requirepass "${REDIS_PASSWORD}"
volumes:
- redis_data:/data
volumes:
couchdb_data:
driver: local
minio_data:
driver: local
redis_data:
driver: local
Start the stack:
docker compose up -d
The first startup takes a minute or two as all services initialize. Watch the logs:
docker compose logs -f --tail=50
Once everything is healthy, Budibase will be accessible at http://your-server-ip:10000.
Reverse Proxy Configuration
Don’t expose Budibase directly. Put it behind a reverse proxy with HTTPS.
Caddy (recommended)
Add to your Caddyfile:
Nginx
server {
listen 443 ssl http2;
server_name budibase.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/budibase.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/budibase.yourdomain.com/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://localhost:10000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (needed for real-time features)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Initial Setup
- Navigate to
https://budibase.yourdomain.comin your browser - You’ll see the admin account creation screen
- Enter your email, password, and organization name
- Click Create admin user
That’s it — you’re in. No verification email, no phone number, no waiting for approval.
Building Your First App
Budibase shines when you need a quick internal tool. Here’s a practical example: an inventory tracker.
Create the App
- Click Create new app from the dashboard
- Choose Start from scratch (or pick a template)
- Name it “Inventory Tracker”
Set Up the Data
Click Data in the left sidebar. You have two options:
Option A: Use BudibaseDB (built-in)
- Click BudibaseDB → Create new table
- Name it “Items”
- Add columns:
name(Text) — the item namecategory(Options) — define categories like Electronics, Furniture, etc.quantity(Number) — current stocklocation(Text) — where it’s storedlast_updated(Date) — auto-populated
Option B: Connect an external database
- Click Add source → choose PostgreSQL, MySQL, REST API, etc.
- Enter connection details
- Budibase will auto-discover your tables and relationships
Build the Interface
Switch to the Design tab:
- Add a Table component linked to your Items data
- Enable inline editing, sorting, and filtering
- Add a Form component for creating new items
- Use Conditional display to show different views based on user role
Add Automations
Click Automate in the sidebar:
- Trigger: When a row is updated in “Items”
- Condition: If
quantitydrops below 10 - Action: Send an email notification to the team or hit a webhook
Automations can also run on schedules (daily inventory reports), app actions (button clicks), or external webhooks.
Connecting External Data Sources
One of Budibase’s strengths is pulling data from where it already lives:
- PostgreSQL / MySQL / MariaDB / MSSQL — full CRUD operations
- MongoDB — read and write to collections
- REST API — connect to any HTTP endpoint
- Google Sheets — use spreadsheets as a data source
- Airtable — import your existing bases
- S3-compatible storage — file attachments via MinIO or AWS S3
- Oracle Database — enterprise data source support
For each external source, Budibase generates queries automatically but also lets you write custom queries when you need more control.
User Management and RBAC
Budibase has three built-in roles:
- Admin — full platform access, manage users, apps, and settings
- Power — can create and edit apps
- Basic — can use apps they’ve been given access to
Within each app, you can further restrict access at the screen and component level. For example, only show the “Delete” button to Power users, or restrict a data table to show only rows owned by the current user.
SSO Integration
For production deployments, configure SSO under Settings → Auth:
- OpenID Connect — works with Authentik, Keycloak, Auth0, etc.
- Google OAuth — quick setup for Google Workspace teams
- SCIM — automatic user provisioning (Premium feature)
If you’re already running Authentik or Authelia, plug Budibase into your existing identity provider.
Backup Strategy
Budibase stores data across multiple services. A complete backup needs to cover all of them:
#!/bin/bash
# backup-budibase.sh
BACKUP_DIR="/backups/budibase/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Stop services for consistent backup
cd ~/budibase
docker compose stop app-service worker-service
# Backup CouchDB data
docker compose exec -T couchdb-service \
tar czf - /opt/couchdb/data > "$BACKUP_DIR/couchdb.tar.gz"
# Backup MinIO data
docker run --rm \
-v budibase_minio_data:/data \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/minio.tar.gz /data
# Backup Redis (optional — mostly cache, but persistent data exists)
docker run --rm \
-v budibase_redis_data:/data \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/redis.tar.gz /data
# Backup .env file (contains your secrets!)
cp ~/budibase/.env "$BACKUP_DIR/env.backup"
# Restart services
docker compose start app-service worker-service
echo "Backup completed: $BACKUP_DIR"
Budibase also supports app-level export: go to Settings within any app and click Export. This creates a portable file you can import into another Budibase instance.
Performance Tuning
Memory Allocation
The default stack is hungry. If you’re running on a 2GB server, consider these tweaks in your docker-compose.yml:
services:
couchdb-service:
# ... existing config ...
deploy:
resources:
limits:
memory: 512M
redis-service:
# ... existing config ...
command: redis-server --requirepass "${REDIS_PASSWORD}" --maxmemory 128mb --maxmemory-policy allkeys-lru
Disable Analytics
Already done in our compose file (ENABLE_ANALYTICS: "false"), but double-check under Settings → Organisation → Analytics in the Budibase UI.
CouchDB Compaction
CouchDB can grow large over time. Set up periodic compaction:
# Run weekly via cron
curl -X POST http://admin:password@localhost:5984/_compact \
-H "Content-Type: application/json"
Troubleshooting
Services Won’t Start
Check that all environment variables are set:
docker compose config
Missing secrets (especially API_ENCRYPTION_KEY or JWT_SECRET) will cause silent failures.
“502 Bad Gateway” on First Access
The app and worker services take 30-60 seconds to initialize. Check their status:
docker compose ps
docker compose logs app-service --tail=20
CouchDB Clustering Errors
If you see CouchDB cluster warnings in the logs, they’re usually harmless for single-node setups. The Budibase CouchDB image handles single-node configuration automatically.
High Memory Usage
CouchDB and MinIO are the biggest consumers. If memory is tight:
- Ensure CouchDB compaction is running
- Set Redis
maxmemoryas shown above - Consider using an external PostgreSQL database instead of BudibaseDB (CouchDB) for large datasets
App Export/Import Fails
Large apps with many attachments can timeout during export. Use the file-system backup approach instead of the UI export for production backups.
Updating Budibase
cd ~/budibase
docker compose pull
docker compose down
docker compose up -d
Budibase handles database migrations automatically on startup. Still, always back up before updating — especially across major versions.
Check the Budibase changelog before upgrading to catch any breaking changes.
Conclusion
Budibase fills a real gap: it’s powerful enough to replace simple internal tools that would otherwise require a developer, but self-hostable and open-source so your data stays yours. The learning curve is gentle — if you can build a spreadsheet, you can build a Budibase app.
Start with one pain point. That inventory tracker nobody wants to maintain in Excel. The onboarding form that’s still a PDF. The customer lookup tool that’s three browser tabs and a prayer. Build it in Budibase, deploy it on your own server, and move on to the next one.
The platform is actively developed, with new features landing regularly. The community is helpful, the documentation is solid, and the self-hosted experience is first-class. For internal tooling needs, Budibase is one of the best open-source options available.