Get started

Ordered easiest-first, deliberately. Leading with a source build is how a project turns "try it" into an afternoon.

This is alpha software, version 0.5. The shape of stored content is stable and an older build quarantines content it does not understand rather than dropping it. Screens and options are not settled. Read what this is not before deploying it for somebody else.

1. One click

Deploy with Vercel

Vercel clones the repository into the user's own GitHub account, provisions a Neon Postgres database and a Blob store, and asks for one variable — OPB_SETUP_TOKEN, which they invent.

Nothing else is required. The adapter is chosen from whichever backend's environment variables are present, so provisioning the database is the configuration.

Before recommending this to somebody who charges for their work, read Vercel's non-commercial clause for the Hobby plan. It is quoted in full on what it costs.

2. Docker

git clone https://github.com/ShadmanArafin/open_portfolio.git
cd open_portfolio
docker compose up

App and Postgres together, on http://localhost:3000. Change OPB_SETUP_TOKEN and OPB_SECRET_KEY in docker-compose.yml first.

The app alone, against a database you already run:

docker build -t open-portfolio .
docker run -p 3000:3000 -v opb-data:/data \
  -e OPB_POSTGRES_URL="postgres://…" \
  -e OPB_SETUP_TOKEN="a long phrase you invent" \
  -e OPB_SECRET_KEY="a different long phrase" \
  open-portfolio

The volume matters. /data holds uploads, and holds content too if no database is configured. Without it, replacing the container deletes the site. With it, the container is disposable — which is exactly what updating does.

3. From source

Node 20.9 or newer. No database needed; the filesystem backend is the default.

git clone https://github.com/ShadmanArafin/open_portfolio.git
cd open_portfolio
npm ci
npm run dev

http://localhost:3000 sends you to /setup. Content lands in .opb/ — delete the folder to start over.

npm ci, not npm install: a dependency bump once passed against a populated node_modules and failed on a clean install.

Running the tests

npm test          # 686 passing, 7 skipped

The skipped ones need containers:

docker run -d --name opb-pg -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=opb_test \
  -p 55432:5432 postgres:16
docker run -d --name opb-mail -p 1025:1025 -p 8025:8025 axllent/mailpit

TEST_POSTGRES_URL="postgres://postgres:postgres@localhost:55432/opb_test" \
TEST_MAILPIT_URL="http://localhost:8025" npm test

Both the -e and the -p flags matter. Without POSTGRES_DB the image only creates a database called postgres and ninety tests fail with database "opb_test" does not exist. A Mailpit container created without -p reports healthy and is unreachable. Both look like code bugs and neither is.

Setting TEST_POSTGRES_URL with the container stopped is worse than not setting it: the Postgres tests stop skipping and start failing.