FormBuilder
Self-hosted form builder for one workspace with Authentik OIDC sign-in, Prisma/Postgres storage, per-form response ACLs, public form links, webhooks, uploads, and an MCP endpoint.
Portainer Deployment
This repo is ready to deploy as a Portainer stack with docker-compose.yml.
- Create a new git repo and push this directory.
- In Portainer, create a stack from the git repository.
- Copy
.env.exampleto.envin the stack environment and fill the values below. - Deploy the stack.
The app container runs prisma migrate deploy before starting the Next.js standalone server.
Required .env
APP_PORT=3080
POSTGRES_PASSWORD=replace-with-a-strong-password
AUTH_SECRET=replace-with-openssl-rand-base64-32
AUTH_URL=https://forms.example.com
PUBLIC_FORM_URL=https://forms-public.example.com
OIDC_ISSUER=https://authentik.example.com/application/o/formbuilder/
OIDC_CLIENT_ID=replace-with-authentik-client-id
OIDC_CLIENT_SECRET=replace-with-authentik-client-secret
OIDC_PROVIDER_NAME=Authentik
AUTH_BOOTSTRAP_ADMINS=you@example.com
Optional values are documented in .env.example for Redis rate limiting, email, file storage, hCaptcha, and webhook worker auth.
Reverse Proxy
Run the app behind your reverse proxy with HTTP upstream, then terminate TLS at the proxy.
For Nginx Proxy Manager or openresty outside this Compose network:
- Scheme:
http - Forward hostname/IP: the Docker host running this stack
- Forward port:
${APP_PORT}; default3080 - Public URL: set
AUTH_URLto the final external URL, for examplehttps://forms.internal.vyntehome.com
If openresty is attached to the same Docker network as this stack, proxy to http://app:3000 instead of the host-published port.
Minimal openresty location:
location / {
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
502 Bad Gateway means openresty cannot reach the upstream. First verify the app from the proxy host with curl http://127.0.0.1:3080/signin or curl http://<docker-host-ip>:3080/signin.
Separate Builder And Public Forms Domains
To build/manage forms at forms.internal.vyntehome.com and serve published forms at forms.vyntehome.com, point both reverse-proxy hosts to the same app upstream and set:
AUTH_URL=https://forms.internal.vyntehome.com
PUBLIC_FORM_URL=https://forms.vyntehome.com
Keep the Authentik redirect URI on the internal builder domain:
https://forms.internal.vyntehome.com/api/auth/callback/oidc
The public proxy host must forward these paths to the app:
/f/*
/embed.js
/_next/*
/api/forms/*
/api/files/*
Published forms intended for forms.vyntehome.com must use Public visibility. Workspace-only forms require an authenticated session and should be opened on the internal builder domain.
Authentik Setup
Create an OAuth2/OpenID provider in Authentik:
- Provider type: OAuth2/OpenID
- Client type: Confidential
- Redirect URI:
${AUTH_URL}/api/auth/callback/oidc - Scopes:
openid,profile,email - Issuer mode: use the provider's OpenID Configuration Issuer URL
Then create an Authentik application and bind it to that provider. Put the issuer, client ID, and client secret in .env.
The first successful signer becomes an admin. Any emails listed in AUTH_BOOTSTRAP_ADMINS are also promoted on first sign-in.
Persistent Data
The compose stack creates two named volumes:
postgres_data: bundled Postgres databaseuploads: local uploaded files mounted at/app/uploads
For multi-instance deployments, set RATE_LIMIT_DRIVER=redis and provide REDIS_URL. For durable object storage outside the app container, configure the S3 values in .env.example.
Useful Commands
npm ci
npm test
npm run build
docker compose up --build
MCP Endpoint
The MCP endpoint is available at:
POST /api/mcp
Create a token in /app/account, then send requests with:
Authorization: Bearer fb_xxxxxxxxxxxxxxxx
Stack
Next.js 15 App Router, React 19, Auth.js v5, Authentik OIDC, Prisma, Postgres, Tailwind, and Docker Compose.