Repository navigation
Setting Up the Server
The server is the web UI plus everything behind it: the Test Repository, reports and session replay, schedules, notifications and MCP. Run it on one machine for your team.
plum server start runs a Docker Compose stack:
| Service | Port | What it is |
|---|---|---|
postgres |
(internal) | PostgreSQL 16, data in a named Docker volume |
backend |
3001 | Express + Socket.io API. Runs prisma migrate deploy on boot, then starts. |
frontend |
3002 | The SvelteKit UI, built and served by @sveltejs/adapter-node. |
All three use restart: unless-stopped, so the server comes back after a reboot. The database is never touched by plum server commands — only the containers restart.
The wizard asks:
- Where are you setting up Plum? Local machine or Production / server.
- Use the default ports (3001 / 3002)? If yes, anything already on those ports is stopped first (the database is unaffected). If no, you pick custom ports.
- (Production only) Public URL or IP of the backend, and of the UI.
Settings are saved to .plum-server.json in the instance folder, so a later plum server start skips the questions.
The API mounts its routers at bare paths (/auth, /reports, …) and the socket path is /socket.io. A single origin serving both has no way to tell them apart. plum server start refuses an api/ui pair with the same origin, or with mismatched schemes. Keep them on separate ports (the default) or separate hostnames.
| Command | Effect |
|---|---|
plum server start |
Configure (first run) and start the stack |
plum server restart |
Rebuild images and restart, no prompts. Run this after pulling backend or schema changes. |
plum server stop |
Stop the containers. Data is preserved. |
plum server reconfig |
Re-enter the settings without starting |
Flags for scripted setup: --backend-port, --frontend-port, --api-url, --ui-url. --framework also still works, but only as the pre-selected default offered when creating a later project (Settings → Project); it has no effect on your first project, which is chosen on the setup page itself.
Point two hostnames (or a hostname + a port) at the two containers, e.g.
-
plum.example.com→frontend:3002 -
api.plum.example.com→backend:3001
and answer the production URL prompts with those. A minimal Nginx block per host:
location / {
proxy_pass http://127.0.0.1:3002; # or :3001 for the API host
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # needed on the API host for socket.io
}The socket transport falls back to long-polling, so live logs still work behind a proxy that does not forward the WebSocket upgrade — but forwarding it is better.
If the UI is served from an origin the backend doesn't already know, set PLUM_ALLOWED_ORIGINS (comma-separated) in the environment. Compose passes it through to the backend container for both CORS and the socket handshake.
For Google sign-in, plum server passes your API URL to the backend (PLUM_API_URL) and the redirect URI is <API URL>/auth/google/callback. Set PLUM_OAUTH_REDIRECT_URI only if you run your own docker-compose without plum server and can't forward X-Forwarded-Proto / X-Forwarded-Host from your proxy — give it the exact https://api.example.com/auth/google/callback registered on the OAuth client.
The UI bundle is built with a sentinel for the API URL and the entrypoint substitutes the real value on every container start from VITE_API_URL (which plum server writes into docker-compose.override.yml). You can change the API URL and restart without rebuilding the image.
Getting started
Tests
Platform
- Roles and Access
- Google Sign-In
- Test Repository
- Reports and Session Replay
- Retrying Flaky Tests
- Integrations
- AI Agent
- API & MCP
- Activity Logs
- Backup