All posts

Self-Hosting Your ATS: The Complete Guide

March 20, 2026 Joachim KolleAbout the author

A self-hosted applicant tracking system runs on infrastructure you control — your own server, a rented VPS, or a managed cloud platform. You own every byte of candidate data, pay no per-seat fees, and choose exactly where your recruitment database lives. This guide covers the full lifecycle: architecture, deployment, security, backups, ongoing operations, real costs, and GDPR compliance.

Most self-hosting guides for ATS software stop at "install it on a server." That is roughly 10% of what you need to know. The other 90% — securing candidate data, automating backups, applying updates without downtime, and keeping infrastructure costs predictable — is what separates a weekend experiment from a production hiring system. This is the guide that covers all of it.

What Self-Hosting Means for Your Applicant Tracking System

Self-hosting an ATS means deploying the application on infrastructure you manage rather than accessing it through a vendor's cloud platform. The software, the database holding your candidate records, and the file storage containing resumes all run on machines you control.

The practical difference is ownership. With a cloud ATS like Greenhouse, Lever, or Workable, your candidate data lives on the vendor's servers. Cancel your subscription and exporting that data is often incomplete — custom fields, pipeline stages, and interview scorecards rarely survive migration intact. With a self-hosted ATS, the database is yours. A standard pg_dump command exports everything. No vendor can hold your talent pool hostage.

Self-hosting does not mean running a server rack in your office closet. Modern self-hosted applications run inside Docker containers on a $5/month cloud server, a managed platform like Railway, or even a laptop for evaluation. The infrastructure overhead has dropped dramatically in the last five years.

If you are unfamiliar with how applicant tracking systems work, start there. This guide assumes you understand what an ATS does and focuses on the self-hosting dimension.

Why Self-Host Your ATS (and When Not To)

Self-hosting is not the right choice for every organization. Here is an honest breakdown.

When self-hosting makes sense

You need data sovereignty. Candidate resumes, contact information, and interview feedback are sensitive personal data. For companies subject to GDPR, CCPA, or industry-specific regulations (healthcare, finance, government), self-hosting can reduce vendor-related compliance overhead significantly. The ATS vendor is no longer hosting your candidate data, which simplifies your processor landscape — though you may still rely on infrastructure processors such as your cloud host, email provider, or CDN.

You refuse to pay a per-seat tax. Cloud ATS platforms charge $15–$165 per recruiter per month. A 10-person hiring team on a mid-tier platform pays $12,000+ per year. Self-hosted ATS software has zero per-seat fees — your entire team accesses the system without increasing costs. For a detailed cost comparison, see our total cost of ownership analysis.

You want full control over your hiring workflow. Self-hosted means you control the source code. Need a custom pipeline stage, a proprietary scoring algorithm, or an integration with an internal HRIS? Fork the repo and build it. No enterprise tier required.

You care about AI transparency. Under the EU AI Act, many AI systems used in recruitment, employment, and worker management are treated as high-risk and require transparency, documentation, and human oversight. Self-hosted open-source ATS platforms make scoring logic inspectable by design — the source code is public. Reqcore's roadmap includes transparent AI candidate ranking with a visible Matching Logic summary, so recruiters will see exactly why a score was assigned and can verify or override the result. Black-box algorithms in commercial platforms cannot offer this level of auditability.

When cloud makes more sense

  • Your team has zero technical capacity and no plans to develop it
  • You need to be hiring within 24 hours with no setup time
  • Your organization is under 5 people and cost savings are negligible
  • You require vendor-managed support SLAs for compliance reasons

For a deeper comparison of the two approaches, read our self-hosted vs cloud ATS breakdown.

What You Need: Infrastructure Requirements

A self-hosted ATS is less resource-hungry than most people assume. Here are the actual requirements for running a production instance:

RequirementMinimumRecommended
CPU1 vCPU2 vCPU
RAM2 GB4 GB
Disk10 GB20 GB+ (scales with resume uploads)
OSAny Linux, macOS, Windows (WSL2)Ubuntu 22.04 LTS or Debian 12
SoftwareDocker Engine + Docker ComposeSame
NetworkInternet for initial setupStatic IP + domain for production

These requirements handle a hiring team of 10–50 people processing hundreds of applications per month. The database and file storage are the only components that grow with usage, and PostgreSQL handles tens of thousands of candidate records comfortably within these specs.

ProviderPlanMonthly CostBest For
HetznerCX22/CX23 (2 vCPU, 4 GB)~€4–5/monthBest value. EU data centers (GDPR). Check current pricing.
DigitalOceanBasic Droplet (1 vCPU, 2 GB)$12/monthBeginner-friendly interface.
VultrCloud Compute (1 vCPU, 2 GB)~$12/monthGlobal data centers. Verify current pricing.
RailwayHobby$5 minimum monthly usageOne-click deploy. Charges can exceed $5 with higher resource use.

Three Deployment Paths Compared

Not all self-hosting is equal. There are three distinct approaches, each with different trade-offs:

FactorVPS (Manual)Managed Platform (Railway)Local Machine
Setup time1–2 hoursUnder 10 minutes5 minutes
Monthly cost€4–$15$5 minimum usage$0 (your hardware)
Technical skillBasic Linux + DockerClick a buttonInstall Docker
Custom domain + HTTPSYou configure itAutomaticNot applicable
Production-ready?Yes (with hardening)YesNo (evaluation only)
UptimeYou manage itPlatform manages itDepends on your laptop
Best forFull control, EU hostingFast production deployTesting and evaluation

Reqcore supports all three. The same docker compose up -d command works whether you are on a Hetzner VPS in Helsinki, Railway's managed platform, or your MacBook.

Step-by-Step: Deploying a Self-Hosted ATS with Docker

This walkthrough uses Reqcore as the reference implementation. The pattern applies to any Docker-based ATS.

The architecture

A self-hosted ATS typically runs three components inside Docker containers:

┌─────────────────────────────────────────────────┐
│  Your Server                                    │
│                                                 │
│  ┌────────────┐  ┌──────────┐  ┌─────────────┐ │
│  │  ATS App   │  │PostgreSQL│  │   MinIO      │ │
│  │  (Nuxt 4)  │  │   16     │  │ (S3 Storage) │ │
│  │  :3000     │  │  :5432   │  │ :9000/:9001  │ │
│  └─────┬──────┘  └────┬─────┘  └──────┬──────┘ │
│        │              │               │         │
│        └──────────────┴───────────────┘         │
│              Docker Network (internal)          │
│                                                 │
│  Only port 3000 is exposed externally           │
└─────────────────────────────────────────────────┘

Reqcore's Docker Compose runs three services: PostgreSQL 16 for all application data (candidates, jobs, pipeline stages), MinIO for S3-compatible document storage (resumes, cover letters), and the Nuxt 4 application itself. The database and file storage ports are bound to localhost only — they never touch the public internet.

This three-container architecture gives you clear separation of concerns: the application is stateless and can be rebuilt at any time, while your data lives in persistent Docker volumes that survive container restarts and rebuilds. For a broader comparison of self-hosted ATS options that use similar architectures, see our guide to the best open source applicant tracking systems.

The deployment

1. Install Docker on your server. On Ubuntu/Debian:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

2. Clone the repository:

git clone https://github.com/reqcore-inc/reqcore.git
cd reqcore

3. Generate secure credentials. Reqcore includes a setup script that creates cryptographically random passwords for all services:

./setup.sh

This generates a .env file with database passwords, authentication secrets, and storage credentials. Never commit or share this file.

4. Start everything:

docker compose up -d

The first startup takes 2–5 minutes. Docker pulls the PostgreSQL and MinIO images, builds the application using a multi-stage Dockerfile (Alpine-based, non-root user), and runs database migrations automatically.

5. Open your browser at http://your-server-ip:3000, create your account, and set up your organization.

The application itself starts in minutes once Docker and the server are ready. First-time production setup takes longer when you include server provisioning, DNS, TLS, and firewall configuration — budget 1–2 hours for a VPS, or under 10 minutes on a managed platform like Railway.

Adding HTTPS and a custom domain

For production, place a reverse proxy in front of the application. Caddy is the simplest option — it obtains and renews Let's Encrypt certificates automatically:

# Caddyfile
ats.yourcompany.com {
    reverse_proxy localhost:3000
}

Point your DNS A record to your server's IP, start Caddy, and HTTPS is handled. Nginx + Certbot is the alternative for teams already using Nginx.

Securing Your Self-Hosted ATS

Candidate data is sensitive personal information. Security is not optional. Here is what a properly secured self-hosted ATS looks like.

What should ship by default

Reqcore ships with these security defaults out of the box, requiring zero configuration:

Security ControlImplementation
Localhost-bound servicesPostgreSQL, MinIO, and admin tools are never exposed to the internet
CSRF protectionAutomatic via Better Auth
Encrypted tokensAES-256-GCM for OAuth tokens
Rate limitingApplied to authentication and sensitive endpoints
Security headersX-Frame-Options: DENY, X-Content-Type-Options: nosniff, strict Referrer-Policy
Upload validationMIME type verification, file size limits, filename sanitization
Server-proxied downloadsUploaded files pass through the application — direct storage access is blocked
Deny-by-default access controlEvery API endpoint checks org membership and role permissions

If the ATS software you are evaluating does not provide these defaults, that is a red flag. Security should be built in, not bolted on.

What you should add

Firewall. Restrict inbound traffic to SSH (port 22), HTTP (80), and HTTPS (443):

sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw enable

Automatic security updates. On Ubuntu/Debian:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Strong credentials. Use the generated passwords from the setup script. Do not replace them with memorable alternatives.

Keep Docker updated. Docker vulnerabilities affect all containers on the host:

sudo apt update && sudo apt upgrade docker-ce docker-ce-cli containerd.io

Backups, Updates, and Day-2 Operations

Deployment is day 1. Day 2 is where most self-hosting guides stop — and where most self-hosting setups fail. Here is what ongoing operations actually look like.

Backups

Your self-hosted ATS holds candidate data that cannot be recreated. Backups are non-negotiable.

Database backup (covers all candidates, jobs, pipeline data):

docker compose exec db pg_dump -U reqcore reqcore > backup-$(date +%Y%m%d).sql

Automated daily backups via crontab:

# Daily at 2 AM, retain 30 days
0 2 * * * cd /path/to/reqcore && docker compose exec -T db pg_dump -U reqcore reqcore > /backups/reqcore-$(date +\%Y\%m\%d).sql && find /backups -name "reqcore-*.sql" -mtime +30 -delete

Document backup (resumes, cover letters stored in MinIO):

docker cp reqcore_minio:/data ./minio-backup-$(date +%Y%m%d)

Test your restore process before you need it. A backup you have never tested is not a backup.

Updates

Reqcore includes a built-in update system in the web UI — no command line required for routine updates:

  1. Go to Settings → Updates
  2. Click "Create backup first" (recommended)
  3. Click "Update to vX.Y.Z"
  4. Wait ~2 minutes, refresh the page

For teams that prefer the CLI:

git pull origin main
docker compose up --build -d

Database migrations run automatically. Your data is preserved in Docker volumes across rebuilds.

Monitoring

For production instances, set up basic uptime monitoring using a free service like UptimeRobot (free tier: 50 monitors). Point it at your ATS URL and get alerted when the instance goes down.

Check service health anytime with:

docker compose ps

All three containers (app, db, minio) include built-in health checks that Docker monitors continuously.

How much time does maintenance take?

Based on running Reqcore in production: 1–2 hours per month. That covers pulling updates, verifying backups, and occasional log review. This is not a full-time job. It is a recurring task comparable to updating any other self-hosted application.

The Real Cost of Self-Hosting Your ATS

Self-hosting saves money, but dishonest cost comparisons that ignore maintenance are misleading. Here are real numbers:

Cost ItemSelf-Hosted (VPS)Self-Hosted (Railway)Cloud ATS (10 seats)
Software$0 (open-source)$0 (open-source)$12,000–$19,800/yr
Infrastructure€4–$15/mo ($48–$180/yr)$5+/mo ($60+/yr)Included
Domain + DNS~$12/yr~$12/yrIncluded
HTTPS (Let's Encrypt)$0$0 (automatic)Included
Maintenance time1–2 hrs/mo<1 hr/mo0 hrs
3-year total$180–$576 + time$216 + time$36,000–$59,400

The infrastructure cost of self-hosting Reqcore is $5–$15 per month regardless of team size. A 10-person hiring team on a mid-tier cloud ATS pays $1,000–$1,650 per month — and that bill grows with every recruiter added.

The honest trade-off: self-hosting costs time instead of money. Budget 1–2 hours per month for a VPS deployment, or under 1 hour per month on a managed platform. For most organizations, that trade-off saves $10,000+ per year.

Data Ownership and Compliance

Self-hosting your ATS gives you something that no SaaS agreement can fully guarantee: actual data ownership.

What you own

When you self-host, every piece of recruitment data lives in infrastructure you control:

  • Candidate records — Names, emails, phone numbers, application history → PostgreSQL database
  • Documents — Resumes, cover letters, portfolios → MinIO (S3-compatible storage)
  • Pipeline data — Stages, scores, interview feedback, hiring decisions → PostgreSQL database
  • Audit logs — Who accessed what, when → PostgreSQL database

Reqcore stores all candidate data in a standard PostgreSQL database with no proprietary encoding. Running pg_dump exports your complete recruitment database in a format that works with any PostgreSQL-compatible tool. There is no vendor lock-in at the data layer.

GDPR compliance advantages

Self-hosting can simplify GDPR compliance in three concrete ways:

  1. Data residency. Choose a hosting provider in your jurisdiction. A Hetzner server in Helsinki means candidate data stays in the EU, on infrastructure you control. This can reduce cross-border transfer complexity under Schrems II — though you still need to verify that all supporting services (email, monitoring, CDN) also stay within scope.
  2. Right to deletion. When a candidate requests data deletion under GDPR Article 17, you have direct control over deletion workflows in your own database. Self-hosting gives you direct control over operational deletion, though GDPR erasure still needs to be managed carefully across live systems, logs, and backups — regulators expect erasure to extend to backups as they are cycled out.
  3. Fewer processors in your chain. With a cloud ATS, the vendor is a data processor under GDPR, requiring a Data Processing Agreement, security audits, and trust. Self-hosting removes the ATS vendor as host, but it does not necessarily remove all processors from your stack — your infrastructure provider, email service, and other supporting tools may still qualify as processors under EDPB guidance.

Data portability

The difference between self-hosted and cloud ATS becomes starkest when you need to leave. Cloud ATS exports are typically incomplete — custom fields, pipeline configurations, and scoring data rarely survive. Reqcore uses PostgreSQL and S3, which are industry standards. Your data is portable from day one, with no export limitations.

Frequently Asked Questions

How much does it cost to self-host an ATS?

The software is free (open-source). Infrastructure costs range from $5 to $15 per month for a server that handles teams of 10–50 people. Add ~$12 per year for a domain name. Total first-year cost is $72–$192, compared to $12,000+ for a cloud ATS with 10 seats.

Is a self-hosted ATS secure enough for candidate data?

A properly configured self-hosted ATS is more secure than most cloud alternatives for one reason: reduced attack surface. Reqcore binds all internal services (database, file storage) to localhost by default, encrypts tokens with AES-256-GCM, and enforces deny-by-default access control on every endpoint. Your data never travels to a third-party server.

Do I need a dedicated DevOps engineer?

No. Modern self-hosted applications run inside Docker containers. If you can copy five commands into a terminal, you can deploy a self-hosted ATS. Updates and backups are handled through a web UI. Budget 1–2 hours per month for maintenance.

Can I migrate from a cloud ATS to self-hosted?

Yes. Export your candidate data from the cloud ATS (typically as CSV or JSON), then import it into your self-hosted PostgreSQL database using standard database tools. The complexity depends on how proprietary your current vendor's data format is — which is exactly the vendor lock-in problem that self-hosting prevents for the future.

What happens if something breaks?

Your data is stored in Docker volumes that persist across container failures. If the application crashes, docker compose up -d restarts it. If an update fails, roll back with git checkout v1.0.0 && docker compose up --build -d. Pre-update backups (available via the UI) provide an additional safety net.

The Bottom Line

Self-hosting your applicant tracking system is no longer an advanced engineering project. A Docker Compose setup runs a production-grade ATS on a $5/month server with minimal active setup time. The trade-off is honest: you invest 1–2 hours per month in maintenance, and in return you own your candidate data permanently, pay no per-seat fees, and maintain full control over your hiring infrastructure.

The organizations that benefit most are those with data sovereignty requirements, growing teams that refuse to pay an expanding SaaS bill, and anyone who wants to inspect the code behind AI-driven candidate scoring. Check our product roadmap to see what is coming next. For those teams, self-hosting is not the difficult option — it is the rational one.


Reqcore is an open-source (AGPL-3.0) applicant tracking system with no per-seat pricing and full data ownership. Deploy with Docker — try the live demo or read the self-hosting documentation.

About Joachim Kolle

Joachim Kolle

Founder of Reqcore

Joachim Kolle is the founder of Reqcore. He works hands-on with open source software, programming, ATS software, and recruiting workflows.

He writes and reviews content about self-hosted ATS, data ownership, and practical hiring operations.

About the authorLinkedIn profile

Ready to own your hiring?

Reqcore is the open-source ATS you can self-host. Transparent AI, no per-seat fees, full data ownership.

Keep reading