Self-host
One container. Data lives in an embedded Postgres on a volume. You need Docker and a 32-byte secret that encrypts provider keys.
Generate the secret:
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Run the image:
docker run -d \
--name openprofit \
-p 3000:3000 \
-v openprofit_data:/app/data \
-e SECRET_KEY=your_secret_here \
-e APP_URL=http://localhost:3000 \
ghcr.io/lobbystack/openprofit:latest
Open http://localhost:3000/login and enter your email. Without an email provider the sign-in link prints in the container log:
docker logs openprofit
Set RESEND_API_KEY and EMAIL_FROM to send links and the weekly email by mail.
Your own Postgres
Point DATABASE_URL at a postgres:// URL and the embedded database is not used. The schema is applied on start.
Updating
Pull the new image and recreate the container. The volume keeps your data; schema changes apply on start.
docker pull ghcr.io/lobbystack/openprofit:latest
docker rm -f openprofit
Then run the docker run command again.
Railway
The hosted version runs this same image on Railway with a Railway Postgres. Create a service from the image, add a Postgres database, set DATABASE_URL to the database's URL and SECRET_KEY and APP_URL as above.