Overview
Reactive Resume can be self-hosted using Docker in a matter of minutes, and this guide will walk you through the process. Here are some of the services you’ll need to get started:PostgreSQL
Stores accounts, resumes, and application data.
Gotenberg
Generates PDFs by rendering a special print route.
Email (optional)
SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
Storage (optional)
Use S3-compatible storage, or local persistent storage via
/app/data.- Docker Hub:
amruthpillai/reactive-resume:latest - GitHub Container Registry:
ghcr.io/amruthpillai/reactive-resume:latest
Minimum requirements
Docker + Docker Compose
Docker Engine + Docker Compose plugin (or Docker Desktop).
Compute
2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Gotenberg run on the same host).
Storage
Enough for Postgres + uploads (start with 10-20 GB and scale as needed).
Quickstart using Docker Compose
Create a new folder (for examplereactive-resume/) with:
compose.yml.env- a persistent data directory for uploads (for example
./data)
1
Create your .env
Start by creating a
.env file next to your compose.yml..env
2
Generate AUTH_SECRET
Generate a strong secret and paste it into
AUTH_SECRET.3
Create compose.yml
This setup runs Postgres + Gotenberg + Reactive Resume on a private Docker network.
4
Start the stack
APP_URL (for the example above: http://localhost:3000).How startup works (database migrations)
On every start, the server automatically runs database migrations before serving traffic. If migrations fail (usually due to a DB connection issue), the container will exit with an error.
Environment variables
Required
APP_URLDATABASE_URLGOTENBERG_ENDPOINTAUTH_SECRET
Optional
- SMTP (
SMTP_*) - Social auth (
GOOGLE_*,GITHUB_*) - S3 storage (
S3_*) - Feature flags (
FLAG_*)
Server
Server
TZ: Sets the container timezone (affects logs and server-side timestamps). Recommended:Etc/UTC.APP_URL: Canonical/public URL for your instance (used for absolute URLs, redirects, and auth flows). If behind a reverse proxy, set this to your public HTTPS URL (for example,https://resume.example.com).PRINTER_APP_URL(optional): Overrides the base URL used when rendering the print route for Gotenberg. Defaults toAPP_URL. Useful when Gotenberg must access the app via a different internal URL (for example,http://host.docker.internal:3000).
Printer (Gotenberg)
Printer (Gotenberg)
GOTENBERG_ENDPOINT: Base URL where Reactive Resume reaches Gotenberg. In Compose:http://gotenberg:3000.GOTENBERG_USERNAME/GOTENBERG_PASSWORD(optional): Use if Gotenberg is configured with Basic Auth (recommended only if Gotenberg is reachable outside your private network).
Database (PostgreSQL)
Database (PostgreSQL)
DATABASE_URL: Postgres connection string in the formatpostgresql://USER:PASSWORD@HOST:PORT/DATABASE.- In Docker Compose, set
HOSTto the Postgres service name (e.g.postgres), notlocalhost.
- In Docker Compose, set
Authentication
Authentication
AUTH_SECRET: Secret used to secure authentication. Changing it invalidates existing sessions.Generate with:GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET (optional): Enables Google sign-in.GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET (optional): Enables GitHub sign-in.Custom OAuth provider (optional):OAUTH_PROVIDER_NAME: Display name in the UIOAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET: Required for any custom OAuth providerOAUTH_SCOPES: Space-separated scopes (defaults toopenid profile email)
- Option A — OIDC Discovery (preferred): Set
OAUTH_DISCOVERY_URLto your provider’s.well-known/openid-configurationURL - Option B — Manual URLs: Set all three:
OAUTH_AUTHORIZATION_URL,OAUTH_TOKEN_URL, andOAUTH_USER_INFO_URL
Email (SMTP, optional)
Email (SMTP, optional)
If SMTP is not configured, the app logs emails to the server console instead of sending them.
SMTP_HOST: SMTP host (if empty, email sending is disabled).SMTP_PORT: Usually465(implicit TLS) or587(STARTTLS).SMTP_USER/SMTP_PASS: SMTP credentials.SMTP_FROM: Default from address (for example,Reactive Resume <[email protected]>).SMTP_SECURE:"true"or"false"(string). Match your provider settings.
Storage (S3 or local)
Storage (S3 or local)
- Default (local): If all
S3_*values are empty, uploads are stored under/app/data. Mount it to persistent storage (for example./data:/app/data) or uploads can be lost on container recreation. - S3/S3-compatible: Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.):
S3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYS3_REGIONS3_ENDPOINT(for S3-compatible providers; may be blank for AWS depending on your setup)S3_BUCKETS3_FORCE_PATH_STYLE: Controls how the bucket is addressed in URLs. Defaults to"false".- Set to
"true"for path-style URLs (https://s3-server.com/bucket). Common with MinIO, SeaweedFS, and other self-hosted S3-compatible services. - Set to
"false"for virtual-hosted-style URLs (https://bucket.s3-server.com). Common with AWS S3, Cloudflare R2, and most cloud providers.
- Set to
Feature Flags
Feature Flags
FLAG_DEBUG_PRINTER: Bypasses the printer-only access restriction (useful when debugging/printer/{resumeId}). Recommended: keep"false"in production.FLAG_DISABLE_SIGNUP: Disables new signups (web app and server). Useful for private instances.
Updating your installation
To update your Reactive Resume installation to the latest available version, follow these steps:-
Pull the latest images for all services defined in your Docker Compose file.
-
Restart the containers to run the new images.
-
(Optional) Remove old, unused Docker images to free up disk space.
Backups (recommended)
Regular backups are essential to protect your data. Reactive Resume stores data in two places: the PostgreSQL database and file uploads (either local storage or S3).Database backups
Your PostgreSQL database contains all user accounts, resumes, and application data. For self-hosted deployments, you can usepg_dump to create periodic backups of your database and store them in a secure location. Many hosting providers also offer automated backup solutions for managed PostgreSQL instances, which handle scheduling, retention, and restoration for you.
Upload backups
If you’re using local storage (the./data directory), include this directory in your regular backup routine. A simple approach is to use rsync or a similar tool to copy the directory to a remote server or cloud storage.
If you’re using S3-compatible storage, consider enabling versioning on your bucket to protect against accidental deletions. Most S3 providers also support lifecycle rules for automatic cleanup of old versions and cross-region replication for disaster recovery.
Health Checks
Reactive Resume exposes a health check endpoint at/api/health that verifies the application and its dependencies are functioning correctly. If any critical service (such as the database connection) fails, the health check will return an unhealthy status.
How it works
The Docker Compose configuration includes a health check that periodically calls the/api/health endpoint:
docker compose ps or docker ps.
Reverse proxy integration
Most reverse proxies (such as Traefik, Caddy, or nginx with upstream health checks) can use Docker’s health status to make routing decisions:- Healthy containers receive traffic as normal
- Unhealthy containers are automatically removed from the load balancer pool
Manually checking health
You can manually verify the health of your Reactive Resume instance:Troubleshooting
The app container exits immediately
The app container exits immediately
- Common cause: database migrations failed (often a bad
DATABASE_URL). - What to do:
When the app container exits right away, you’ll want to check the logs for more information about the error. Run the following command to view real-time logs from the Reactive Resume container:
Can't sign in / redirects loop / cookies don't stick
Can't sign in / redirects loop / cookies don't stick
PDF export fails / printing is stuck
PDF export fails / printing is stuck
- Common cause: Reactive Resume can’t reach Gotenberg or Gotenberg can’t reach your app.
- Checks:
GOTENBERG_ENDPOINTshould usually behttp://gotenberg:3000in Compose.- If you use
PRINTER_APP_URL="http://host.docker.internal:3000", ensureextra_hosts: host-gatewayis present for Gotenberg.
Uploads disappear after restart
Uploads disappear after restart
- Cause: you didn’t mount persistent storage for
/app/data(when not using S3). - Fix: add a volume mount like
./data:/app/dataand redeploy.
Emails aren't being delivered
Emails aren't being delivered
- Expected behavior: if SMTP vars are empty, the app logs emails to the console instead.
- Fix: configure SMTP and verify your provider’s TLS/port settings.
S3 storage error: ENOTFOUND bucket.endpoint
S3 storage error: ENOTFOUND bucket.endpoint
- Common cause: The S3 client is using virtual-hosted-style addressing (prepending the bucket name to the endpoint), but your S3-compatible storage expects path-style addressing.
- Symptom: Error message like
getaddrinfo ENOTFOUND mybucket.s3-server.comwhen your endpoint iss3-server.com. - Fix: Set
S3_FORCE_PATH_STYLE="true"in your environment. This is required for most self-hosted S3-compatible services like MinIO, SeaweedFS, etc.