Why self-host a resume builder
SaaS resume tools monetise your career history, limit template access or charge for PDF downloads. Self-hosting Reactive Resume inverts the model: your resumes, photos and exports stay in a Docker volume on your VPS, exportable at any time via the UI or REST API, and shareable under your own domain. No template limits, no watermarks, no subscription dependency.
Self-hosting also gives you full control over the sensitive data a resume contains — personal contact details, employment history, skills. This data never transits through or is stored by a third party.
What you get with a self-hosted Reactive Resume
- Browser-based drag-and-drop visual editor — no app to install, works on any device.
- Professionally designed ATS-friendly templates — multiple layouts for different industries and experience levels.
- Client-side PDF export since v5.1.0 — no Chrome, Puppeteer or Browserless server-side.
- Public resume URL — share your profile on a readable, always-updated link.
- Multiple resumes per account — maintain separate CVs for different roles or languages.
- REST API — create and export resumes programmatically for HR tool integration.
- Local storage — uploads (photos, portfolio images) written to a Docker volume, no S3 needed.
- Optional OAuth authentication — connect a GitHub or Google account by adding the corresponding API keys to the configuration.
Requirements
A ServOrbit VPS with Ubuntu 24.04 and at least 1 GB RAM. The Node.js application idles around 300 MB and PostgreSQL 16 adds 100–200 MB — a 1 GB VPS handles a personal or small-team deployment comfortably.
Docker and Docker Compose are provisioned automatically by AWX at deploy time. A domain name is required: Reactive Resume anchors its authentication URL (APP_URL) at first start and cannot function behind a bare IP address.
If you plan to enable email-based account verification, an outgoing SMTP server is recommended. Without SMTP, the verification link is printed to Docker logs — practical for solo use, less suitable if you open access to other users.
Deploy Reactive Resume on your VPS
Choose your domain before deploying
Reactive Resume embeds
APP_URLin authentication tokens and OAuth callbacks at first start — changing the domain afterwards breaks all sessions and requires deleting the PostgreSQL volume. Create your DNS A record (e.g.cv.yourdomain.com) and wait for propagation before deploying.One-click deploy from the ServOrbit Marketplace
Open your ServOrbit control panel, go to Marketplace → Collaboration & Productivity → Reactive Resume, enter your domain and click Deploy. AWX installs Docker, generates a random auth secret, configures nginx with Let's Encrypt TLS and starts both containers (app + PostgreSQL) in under two minutes.
Create your account and verify your email
Navigate to
https://cv.yourdomain.comand click Sign Up to create your account with email and password. Without SMTP configured, the verification link is printed to the Docker logs:docker compose logs reactive-resume | grep verify. Click the link to activate your account.Create your first resume
Click the + button to create a new resume. Choose a template from the library, fill in your sections (Summary, Experience, Education, Skills) in the left panel and preview in real time on the right. Click Download to export a PDF — generation is fully client-side, no waiting for a print server.
Configure OAuth authentication (optional)
To allow sign-in via GitHub or Google, add the corresponding environment variables to your deployment configuration:
GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET,GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET. Then restart the containers withdocker compose restart. The social login buttons appear automatically on the sign-in page as soon as the variables are present.
The v5.1.0 shift: client-side PDF, no print server
Before v5.0, Reactive Resume depended on Browserless or a headless Chromium instance to generate PDFs server-side — adding 700 MB to 1 GB of RAM overhead and a dependency to maintain. Since v5.1.0, PDF generation is handled by @react-pdf/renderer, a JavaScript library running directly in the user's browser. The server only receives a document-save request — no Chrome, no Puppeteer, no print service required. This is what reduces the minimal stack to two lightweight containers.
Keep your auth secret safe
The ServOrbit deployment automatically generates a random value for SECRET_KEY. Never reuse a predictable value or share one between multiple instances. If you suspect a compromise, regenerate this key and restart the containers: all active sessions will be invalidated, forcing users to sign in again — this is the expected behaviour.
Use the REST API to automate your exports
Reactive Resume exposes a full REST API, accessible at /api/v3. You can create resumes, update them and trigger PDF exports programmatically — useful for integrating resume generation into an HR workflow or an automated application pipeline.
To authenticate, retrieve a JWT token via POST /api/v3/auth/login with your credentials. Then pass this token in the Authorization: Bearer <token> header of each request.
Example — list your resumes:
curl -s -H "Authorization: Bearer <your-token>" \
https://cv.yourdomain.com/api/v3/resume | jq '.[].title'Full API documentation is available in the official repository.
Backup and data portability
Two Docker volumes hold the entire instance: reactive_resume_db for the PostgreSQL database (accounts, resumes, settings) and reactive_resume_data for uploaded files (profile photos, portfolio images). Minimal backup:
docker run --rm \
-v reactive_resume_db:/data \
-v $(pwd):/backup \
alpine tar czf /backup/db.tar.gz /dataFor a direct SQL export: docker exec db pg_dump -U postgres postgres > backup.sql
Restore by mounting the volume on a fresh PostgreSQL container configured with the same public URL. Schedule these backups with a daily cron or use your VPS volume snapshot service for automatic coverage.
Troubleshooting common errors
Incorrect APP_URL after a domain change. Symptom: OAuth callbacks fail or the verification link points to the old domain. Fix: update APP_URL in the configuration, then delete the PostgreSQL volume (docker volume rm reactive_resume_db) and restart. All data will be lost — plan this step before creating production accounts.
App container restarting in a loop. Check the logs with docker compose logs reactive-resume. The most common error is a refused PostgreSQL connection (Connection refused to db:5432): the app container starts before PostgreSQL is ready. Wait 30 seconds and run docker compose up -d again.
PDF generated with incorrect layout. PDF rendering is handled client-side by @react-pdf/renderer and depends on your browser's rendering engine. If the output looks off, try a different browser (recent Chrome or Firefox). Old browsers or extensions that alter fonts can affect rendering.
secretOrPrivateKey must have a value error at startup. The SECRET_KEY variable is missing from the configuration. Add it with a random value of at least 32 characters: openssl rand -hex 32 generates a suitable value.
Public resume URL returning 404. Check that the resume is marked as public in its settings. If it is, check your nginx configuration: the location /r/ block must proxy to the Next.js frontend, not to the API.