Skip to content
Feeblo Docs
Esc
navigateopen⌘Jpreview
On this page

Self-hosting Overview

What running Feeblo yourself involves: the four services, what they require, and how traffic flows between them.

Self-hosted Feeblo is four containers: the API server, the dashboard web app, PostgreSQL, and Redis. Both app containers ship as prebuilt images, so a working deployment needs a compose file, a handful of environment variables, and a machine with Docker. Your data stays in your database, under your backup schedule, behind your firewall.

What you run

Service Image Job
server ghcr.io/g3root/feeblo-server HTTP API: auth, dashboard RPC, widget endpoints, webhooks, email
web ghcr.io/g3root/feeblo-web Astro app serving the dashboard and every public feature board
pg pgvector/pgvector:pg17 PostgreSQL 17 with the pgvector extension
redis redis:7-alpine Shared store for rate limiting across server replicas

Images publish to GHCR automatically on pushes to main and on version tags. Pin an exact tag in production; latest moves whenever main moves.

Requirements

Runtime. Docker Engine plus Docker Compose v2 covers everything in this section. If you build from source instead of using the images, you need Node.js ^26.4.0 and pnpm ^11.0.3.

Database. Migrations run CREATE EXTENSION IF NOT EXISTS vector and add a vector(1536) column to the post table, so your PostgreSQL instance must offer the pgvector extension even if you never turn embeddings on. The compose stack satisfies this with the pgvector/pgvector:pg17 image. Bring-your-own databases work as long as the extension does.

Redis. One server instance rate-limits in memory and runs fine without Redis. Once you run more than one replica, point REDIS_URL at a shared instance so limits count across all of them.

Domain. One root domain carries everything. The dashboard lives on the app subdomain, the API on its own host, and each public board gets a subdomain of its own (feedback.yourcompany.com, acme.yourcompany.com). APP_ROOT_DOMAIN names that root.

Email. Sign-up requires clicking a verification link by default (AUTH_EMAIL_VERIFICATION_REQUIRED defaults to true), so SMTP must deliver mail before anyone can finish creating an account. Set SMTP_HOST, SMTP_PORT, and SMTP_FROM_ADDRESS before first boot.

Secrets. Compose refuses to start until AUTH_ENCRYPTION_KEY is set. This key encrypts auth data and integration credentials at rest, and changing it later invalidates existing sessions. Generate one with openssl rand -hex 32.

Ports

Compose publishes four ports, each adjustable through .env:

Variable Default Serves
SERVER_PORT 8080 API server
WEB_PORT 4321 Dashboard and public boards
POSTGRES_PORT 5432 PostgreSQL (drop the mapping on public hosts)
REDIS_PORT 6379 Redis (same)

The server container always listens on 8080 internally; the image pins the port so the healthcheck and reverse proxies agree with the compose mapping. If host port 8080 is already taken, edit the left side of the ports mapping in the compose file and leave SERVER_PORT unset: compose feeds that variable into both sides, and setting it makes the mapping point at a port nothing listens on.

How the pieces connect

browser ──▶ web :4321      dashboard on app.<root>, boards on <site>.<root>
browser ──▶ server :8080   sign-in callbacks, RPC calls from the dashboard,
                           widget requests, webhook payloads

web ──▶ server             server-side requests follow API_URL
server ──▶ pg              DATABASE_URL
server ──▶ redis           REDIS_URL (rate-limit counters)
server ──▶ outbound        SMTP for email, S3-compatible storage for media

Browsers reach web and server directly, which is why APP_URL and API_URL must be real, reachable origins. The web container routes by hostname: any subdomain except app renders the public board registered for that subdomain, and app (or no subdomain) serves the dashboard. Because browsers hit both containers, cookies land correctly only when both URLs share APP_ROOT_DOMAIN.

Startup order takes care of itself: server waits for pg and redis to pass their healthchecks, and web waits for server. One docker compose up -d brings the stack up in the right order.

Next steps

Was this page helpful?