Ein Vergleich von Open-Source No- und Low-Code Plattformen die man selber hosten kann.
Use Cases
Ich habe mir drei Use Cases als Ziel gesetzt:
- Wir bauen eine Web-App die Daten aus einer API konsumiert und in funktionsgerechten Tabellen darstellt.
- Wir bauen Tabellen aus Excel Dateien und wollen diese schick darstellen.
- Wir sind App-Architekt und wollen eine neue Anwendung mit Workflows erstellen.
Die Kandidaten
Mit Hilfe von AI und Websuche finden wir mögliche Standardlösungen.
| Name | Erste Bewertung nach HP Studium | Lizenz |
|---|---|---|
| Budibase | Viele Features in Community Edition gesperrt | GPLv3 + Proprietär |
| Tooljet | Viele Features in Community Edition gesperrt | AGPL-3.0 License |
| Nocobase | Einige Features in Community Edition gesperrt | |
| Appsmith | Einige Features in Community Edition gesperrt | Apache-2.0 |
| Baserow | Einige Features in Community Edition gesperrt | |
| Grommet | Anderer Use Case, fällt raus | - |
| Sail | Anderer Use Case, fällt raus | - |
| rowy | Anderer Use Case, fällt raus | - |
| Directus | Anderer Use Case, fällt raus | - |
Quick-and-Dirty: Live Demos
Wir probieren alle Tools mal kurz aus.
Dazu erstellen wir entsprechende compose Projekte und fahren diese dann auf unserem Server hoch = Live Demo.
Appsmith
Appsmith lässt sich sehr einfach einrichten.
Den Unterordner “stacks” legen wir zuvor an.
compose.yml
services:
appsmith:
image: index.docker.io/appsmith/appsmith-ce
container_name: appsmith
#ports:
# - "80:80"
# - "443:443"
volumes:
- ./stacks:/appsmith-stacks
networks:
- caddy
restart: unless-stopped
labels:
- com.getarcaneapp.arcane.updater=false
- "traefik.enable=true"
- "traefik.docker.network=caddy"
- "traefik.http.routers.appsmith.rule=Host(`appsmith.handtrixxx.com`)"
- "traefik.http.routers.appsmith.entrypoints=websecure"
- "traefik.http.routers.appsmith.tls.certresolver=letsencrypt"
- "traefik.http.routers.appsmith.service=appsmith"
- "traefik.http.routers.appsmith-http.rule=Host(`appsmith.handtrixxx.com`)"
- "traefik.http.routers.appsmith-http.entrypoints=web"
- "traefik.http.routers.appsmith-http.middlewares=https-redirect"
- "traefik.http.routers.appsmith-http.service=appsmith"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"
- "traefik.http.services.appsmith.loadbalancer.server.port=80"
networks:
caddy:
external: true
Tooljet
Tooljet bietet viele Optionen, im Endeffekt sind es aber auch bloß eine compose.yml und eine .env Datei.
compose.yml
name: Tooljet app
services:
tooljet:
tty: true
stdin_open: true
container_name: Tooljet-app
image: tooljet/tooljet:v3.20.126-lts
platform: linux/amd64
restart: always
env_file: .env
#ports:
# - 80:80
networks:
- default
- caddy
depends_on:
- postgres
environment:
SERVE_CLIENT: "true"
PORT: "80"
command: npm run start:prod
labels:
- "traefik.enable=true"
- "traefik.docker.network=caddy"
- "traefik.http.routers.tooljet.rule=Host(`tooljet.handtrixxx.com`)"
- "traefik.http.routers.tooljet.entrypoints=websecure"
- "traefik.http.routers.tooljet.tls.certresolver=letsencrypt"
- "traefik.http.routers.tooljet.service=tooljet"
- "traefik.http.routers.tooljet-http.rule=Host(`tooljet.handtrixxx.com`)"
- "traefik.http.routers.tooljet-http.entrypoints=web"
- "traefik.http.routers.tooljet-http.middlewares=https-redirect"
- "traefik.http.routers.tooljet-http.service=tooljet"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"
- "traefik.http.services.tooljet.loadbalancer.server.port=80"
postgres:
container_name: ${PG_HOST}
image: postgres:16
restart: always
volumes:
- postgres:/var/lib/postgresql/data
env_file: .env
environment:
- POSTGRES_USER=${PG_USER}
- POSTGRES_PASSWORD=${PG_PASS}
networks:
- default
networks:
default:
caddy:
external: true
volumes:
postgres:
driver: local
driver_opts:
o: bind
type: none
device: ${PWD}/postgres_data
certs:
logs:
fallbackcerts:
.env
# Create .env from this example file and replace values for the environment.
# The application expects a separate .env.test for test environment configuration
# Get detailed information about each variable here: https://docs.tooljet.com/docs/setup/env-vars
TOOLJET_HOST=https://tooljet.handtrixxx.com
LOCKBOX_MASTER_KEY=
SECRET_KEY_BASE=
# DATABASE CONFIG
ORM_LOGGING=all
PG_DB=tooljet_production
PG_USER=postgres
PG_HOST=postgresql
PG_PASS=
# The above postgres values is set to its default state. If necessary, kindly modify it according to your personal preference.
# TOOLJET DATABASE
TOOLJET_DB=tooljet_db
TOOLJET_DB_USER=postgres
TOOLJET_DB_HOST=postgresql
TOOLJET_DB_PASS=
PGRST_DB_URI=postgres://postgres:TfNNf9qb5ScO4MZf@postgresql/tooljet_db
PGRST_HOST=localhost:3002
PGRST_JWT_SECRET=
PGRST_SERVER_PORT=3002
PGRST_DB_PRE_CONFIG=postgrest.pre_config
# Checks every 24 hours to see if a new version of ToolJet is available
# (Enabled by default. Set false to disable)
CHECK_FOR_UPDATES=true
# Checks every 24 hours to update app telemetry data to ToolJet hub.
# (Telemetry is enabled by default. Set value to true to disable.)
# DISABLE_TOOLJET_TELEMETRY=false
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# EMAIL CONFIGURATION
DEFAULT_FROM_EMAIL=hello@tooljet.io
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_DOMAIN=
SMTP_PORT=
# DISABLE USER SIGNUPS (true or false). only applicable if Multi-Workspace feature is enabled
DISABLE_SIGNUPS=
# OBSERVABILITY
APM_VENDOR=
SENTRY_DNS=
SENTRY_DEBUG=
# FEATURE TOGGLE
COMMENT_FEATURE_ENABLE=
ENABLE_MULTIPLAYER_EDITING=true
ENABLE_MARKETPLACE_FEATURE=true
# SSO (Applicable only for Multi-Workspace)
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=
SSO_ACCEPTED_DOMAINS=
SSO_DISABLE_SIGNUPS=
#ONBOARDING
ENABLE_ONBOARDING_QUESTIONS_FOR_ALL_SIGN_UPS=
#session expiry in minutes
USER_SESSION_EXPIRY=2880
#TELEMETRY
DEPLOYMENT_PLATFORM=docker
# Workflow scheduling configuration
# Worker Mode
# Set to 'true' to enable job processing
# Set to 'false' or unset for HTTP-only mode (scaled deployments)
WORKER=true
# Workflow Processor Concurrency (optional)
# Number of workflow jobs processed concurrently per worker
# Default: 5
TOOLJET_WORKFLOW_CONCURRENCY=5
# Workflow Execution Timeout (optional)
# Maximum time (in seconds) for a workflow to execute
# Default: 60 seconds
WORKFLOW_TIMEOUT_SECONDS=60
Baserow
Baserow lässt sich sehr einfach einrichten.
Das Unterverzeichnis “data” sollte man zuvor erstellen.
compose.yml
services:
baserow:
container_name: baserow
image: baserow/baserow:2.1.6
environment:
BASEROW_PUBLIC_URL: "https://baserow.handtrixxx.com"
#ports:
# - "80:80"
# - "443:443"
volumes:
- ./data:/baserow/data
networks:
- caddy
labels:
- "traefik.enable=true"
- "traefik.docker.network=caddy"
- "traefik.http.routers.baserow.rule=Host(`baserow.handtrixxx.com`)"
- "traefik.http.routers.baserow.entrypoints=websecure"
- "traefik.http.routers.baserow.tls.certresolver=letsencrypt"
- "traefik.http.routers.baserow.service=baserow"
- "traefik.http.routers.baserow-http.rule=Host(`baserow.handtrixxx.com`)"
- "traefik.http.routers.baserow-http.entrypoints=web"
- "traefik.http.routers.baserow-http.middlewares=https-redirect"
- "traefik.http.routers.baserow-http.service=baserow"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"
- "traefik.http.services.baserow.loadbalancer.server.port=80"
networks:
caddy:
external: true
Budibase
Budibase ist etwas komplizierter, aber machbar.
compose.yml
Die Labels sind nur erfrderlich, wenn man Traefik nutzen will.
services:
app-service:
restart: unless-stopped
image: budibase/apps:latest
container_name: bbapps
networks:
- default
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: "true"
REDIS_URL: redis-service:6379
REDIS_PASSWORD: ${REDIS_PASSWORD}
REDIS_USERNAME: ${REDIS_USERNAME:-}
LITELLM_URL: http://litellm-service:4000
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
BB_ADMIN_USER_EMAIL: ${BB_ADMIN_USER_EMAIL}
BB_ADMIN_USER_PASSWORD: ${BB_ADMIN_USER_PASSWORD}
PLUGINS_DIR: ${PLUGINS_DIR}
OFFLINE_MODE: ${OFFLINE_MODE:-}
depends_on:
- worker-service
- redis-service
- litellm-service
# volumes:
# - /some/path/to/plugins:/plugins
worker-service:
restart: unless-stopped
image: budibase/worker
container_name: bbworker
networks:
- default
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}
REDIS_USERNAME: ${REDIS_USERNAME:-}
LITELLM_URL: http://litellm-service:4000
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
OFFLINE_MODE: ${OFFLINE_MODE:-}
depends_on:
- redis-service
- minio-service
- litellm-service
minio-service:
restart: unless-stopped
image: minio/minio
networks:
- default
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
networks:
- default
- caddy
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
- app-service
- couchdb-service
labels:
- "traefik.enable=true"
- "traefik.docker.network=caddy"
- "traefik.http.routers.buddy.rule=Host(`buddy.handtrixxx.com`)"
- "traefik.http.routers.buddy.entrypoints=websecure"
- "traefik.http.routers.buddy.tls.certresolver=letsencrypt"
- "traefik.http.routers.buddy.service=buddy"
- "traefik.http.routers.buddy-http.rule=Host(`buddy.handtrixxx.com`)"
- "traefik.http.routers.buddy-http.entrypoints=web"
- "traefik.http.routers.buddy-http.middlewares=https-redirect"
- "traefik.http.routers.buddy-http.service=buddy"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true"
- "traefik.http.services.buddy.loadbalancer.server.port=10000"
couchdb-service:
restart: unless-stopped
image: budibase/database:2.1.0
networks:
- default
environment:
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
- COUCHDB_USER=${COUCH_DB_USER}
- TARGETBUILD=docker-compose
- DATA_DIR=/data
volumes:
- ./couchdb3_data:/data
redis-service:
restart: unless-stopped
image: redis
networks:
- default
command: redis-server --requirepass "${REDIS_PASSWORD}"
volumes:
- ./redis_data:/data
litellm-service:
restart: unless-stopped
image: docker.litellm.ai/berriai/litellm:1.80.15-stable.1
#ports:
# - "${LITELLM_PORT:-4000}:4000"
volumes:
- ./litellm_config.yaml:/app/config.yaml
environment:
DATABASE_URL: "postgresql://${LITELLM_DB_USER}:${LITELLM_DB_PASSWORD}@litellm-db:5432/${LITELLM_DB_NAME}"
STORE_MODEL_IN_DB: "True"
LITELLM_REASONING_AUTO_SUMMARY: "true"
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
LITELLM_SALT_KEY: ${LITELLM_SALT_KEY}
command: ["--config", "/app/config.yaml"]
depends_on:
- litellm-db
networks:
- default
healthcheck:
test:
[
"CMD-SHELL",
"wget --no-verbose --tries=1 http://localhost:4000/health/liveliness || exit 1",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
litellm-db:
restart: unless-stopped
image: postgres:16
environment:
POSTGRES_DB: ${LITELLM_DB_NAME}
POSTGRES_USER: ${LITELLM_DB_USER}
POSTGRES_PASSWORD: ${LITELLM_DB_PASSWORD}
volumes:
- ./litellm_data:/var/lib/postgresql/data
networks:
- default
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -d ${LITELLM_DB_NAME} -U ${LITELLM_DB_USER}",
]
interval: 1s
timeout: 5s
retries: 10
networks:
caddy:
external: true
.env
auch wenn der “REDIS_USERNAME” sich setzen lässt, fährt die Anwendung dann nicht ohne weiteres Zutun hoch. Deshalb diesen zunächst als Kommentar stehen lassen.
# Use the main port in the builder for your self hosting URL, e.g. localhost:10000
MAIN_PORT=10000
# This section contains all secrets pertaining to the system
# These should be updated
# #openssl rand -hex 32
API_ENCRYPTION_KEY=
JWT_SECRET=
MINIO_ACCESS_KEY=
MINIO_SECRET_KEY=
COUCH_DB_PASSWORD=
COUCH_DB_USER=
REDIS_PASSWORD=
#REDIS_USERNAME=budiredis
INTERNAL_API_KEY=
LITELLM_MASTER_KEY=
LITELLM_SALT_KEY=
# This section contains variables that do not need to be altered under normal circumstances
APP_PORT=4002
WORKER_PORT=4003
MINIO_PORT=4004
COUCH_DB_PORT=4005
COUCH_DB_SQS_PORT=4006
REDIS_PORT=6379
BUDIBASE_ENVIRONMENT=PRODUCTION
SQL_MAX_ROWS=
LITELLM_PORT=4000
LITELLM_DB_NAME=litellm
LITELLM_DB_USER=llmproxy
LITELLM_DB_PASSWORD=
# An admin user can be automatically created initially if these are set
BB_ADMIN_USER_EMAIL=
BB_ADMIN_USER_PASSWORD=
# A path that is watched for plugin bundles. Any bundles found are imported automatically/
PLUGINS_DIR=
ROLLING_LOG_MAX_SIZE=
litellm_config.yaml
Ohne diese Datei fährt Budibase nicht hoch, der Inhalt muss aber nur semantisch korrekt sein. Also lassen sich die folgenden Beispielwerte einfach 1:1 übernehmen.
model_list:
- model_name: azure-gpt-4o
litellm_params:
model: azure/<your-azure-model-deployment>
api_base: os.environ/AZURE_API_BASE # runs os.getenv("AZURE_API_BASE")
api_key: os.environ/AZURE_API_KEY # runs os.getenv("AZURE_API_KEY")
api_version: "2025-01-01-preview"