How the platform ships
TheDockerfile is a multi-stage build that produces one image:
- Builds the portal (
client/portal) into static files. - Builds a static Go binary.
- Ships both in a
scratchimage. The Go binary serves the API and the portal’s static files on port 8080.
Prerequisites
- A Google Cloud account with billing enabled, plus the
gcloudCLI - A Neon account
- A SendGrid account (or SMTP credentials)
- Your fork of the harp repo on GitHub
Step 1: Create the Google Cloud project
us-south1) and grab the project number:
COMPUTE_SA) is what Cloud Run and Cloud Build will run as. Give it the roles the pipeline needs:
serviceAccountTokenCreator grant is required: the API uses it to sign GCS upload URLs.
Step 2: Create the database on Neon
Create a Neon project and a database. Use the pooled connection string for the API; keep it aside asDB_ADDR. Set DB_MAX_OPEN_CONNS=15 or so, since each Cloud Run instance opens its own pool.
Run the migrations against it once from your machine:
Step 3: Stand up SuperTokens
Use SuperTokens’ managed service, which is free under 5,000 monthly active users. This is what HackUTD runs on. Sign up at supertokens.com, create a core, and copy the connection URI and API key intoSUPERTOKENS_CONNECTION_URI and SUPERTOKENS_API_KEY.
If you expect to exceed the free tier, the core is open source and self-hostable. It needs a Postgres database (a second database in the same Neon project works). Deploy it as its own Cloud Run service:
SUPERTOKENS_CONNECTION_URI and the key becomes SUPERTOKENS_API_KEY. Only the Harp backend should be talking to it.
Step 4: Create the storage bucket
YOUR_APP_URL until step 6 creates the Cloud Run service, so come back and update the CORS file then.
Step 5: Set up continuous deployment
Create an Artifact Registry repository for the images:main that builds with the repo’s Dockerfile, pushes the image tagged with the commit SHA, and deploys it with gcloud run services update.
The Cloud Run console can generate this trigger for you: create the service with “Continuously deploy from a repository”. This is how HackUTD runs it. After that, every merge to main deploys itself.
Step 6: Create the Cloud Run service
The service configuration that has worked in practice:- 1 vCPU, 512 MiB memory, CPU startup boost on
- Concurrency 80, request timeout 300s
- Minimum instances 0, maximum around 20
- Port 8080, public ingress
AUTH_BASIC_USER, AUTH_BASIC_PASS, SUPERTOKENS_CONNECTION_URI, and SUPERTOKENS_API_KEY; a real deployment also sets ENV=prod, APP_URL (the Cloud Run URL), DB_ADDR, GCS_BUCKET_NAME, the email settings, PUBLIC_API_KEY, HACKATHON_NAME, and the VAPID keys (task gen-vapid generates a pair).
Put secrets in Secret Manager rather than plaintext env vars, and reference them from the service:
APP_URL is the service’s own URL, which doesn’t exist until the first deploy. Deploy once, read the URL, set APP_URL, and deploy again. Update the GCS CORS config from step 4 with the same URL.
Step 7: Google OAuth (optional but recommended)
If you want “Sign in with Google”, create an OAuth web client in the Google Auth Platform console:- Configure the consent screen with your team’s support email.
- Create a web application client.
- Add your
APP_URLas an authorized JavaScript origin. - Add
APP_URL/auth/callback/googleas an authorized redirect URI. - Set
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRETon the Cloud Run service.
http://localhost:3000 and its callback) if you want Google login in local dev against the same client.
Step 8: Deploy the marketing site
The marketing site is a separate Vercel project. Import the repo into Vercel, then setHARP_API_BASE_URL to your Cloud Run URL and HARP_PUBLIC_API_KEY to the same value as the backend’s PUBLIC_API_KEY, for both Production and Preview. See The marketing site for details.
Step 9: Verify
- Open
APP_URLand confirm the portal loads. - Create an account, sign in, and complete super-admin onboarding.
- Upload a resume in a test application to confirm the GCS signing path and CORS.
- Send yourself a decision email to confirm the email provider.
curl -H "X-API-Key: ..." APP_URL/v1/public/scheduleto confirm the public API.
Releases and upgrades
Releases are automated with release-please: Conventional Commits onmain accumulate into a release PR, and merging it cuts a tagged release. The CD trigger deploys every merge to main. Adopters upgrade by merging release tags into their fork, see Forking & staying upstream.
A CLI that automates this setup is planned. Until then, this manual path is the supported one.