---
title: "Deploy Cap CAPTCHA | Self-Hosted Proof-of-Work CAPTCHA That Passes Its Health Check"
description: "Self-host Cap on Railway — proof-of-work CAPTCHA, pinned, health-checked."
category: "Authentication"
url: https://railway.com/deploy/cap-captcha-or-self-hosted-proof-of-work
---

# Deploy Cap CAPTCHA | Self-Hosted Proof-of-Work CAPTCHA That Passes Its Health Check

Self-host Cap on Railway — proof-of-work CAPTCHA, pinned, health-checked.

**[Deploy Cap CAPTCHA | Self-Hosted Proof-of-Work CAPTCHA That Passes Its Health Check on Railway](https://railway.com/template/cap-captcha-or-self-hosted-proof-of-work)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/cap-captcha-or-self-hosted-proof-of-work/manifest.json

- **Creator:** Templates Guru
- **Category:** Authentication

## Template content

### Cap

- **Image:** tiago2/cap:3.1.14
- **Health check:** /health
- **Public domain:** Yes

### Valkey

- **Image:** valkey/valkey:9.0.6-alpine
- **Start command:** `/bin/sh -c "exec valkey-server --requirepass $VALKEY_PASSWORD --save 60 1 --maxmemory-policy noeviction --loglevel warning --dir /data"`

## Documentation

# Deploy and Host Cap on Railway

Cap, the open-source proof-of-work CAPTCHA, self-hosted with Valkey. It deploys on the first try: the port, the health check and the volume permissions are set. Every image is pinned.

Nothing to fill in. Open the domain and sign in with the `ADMIN_KEY` from the Cap service's variables.

## About Hosting Cap

Cap protects your forms from bots without tracking your visitors. Instead of picking traffic lights, the visitor's browser solves a small computational puzzle in the background; your server then checks the token with Cap. Two services:

- **Cap**: the dashboard, the challenge API the widget talks to, and the `siteverify` endpoint your backend calls (public)
- **Valkey**: site keys, sessions and rate-limit counters, on its own volume, password-protected

Create a site key in the dashboard, add the widget to your page, and verify the token from your backend with the key's secret.

## Common Use Cases

- **Sign-up, login and contact forms** protected from bots, without Google reCAPTCHA or hCaptcha and their tracking.
- **A CAPTCHA for several sites** from one deployment: one site key per site, each with its own difficulty and allowed origins.
- **Rate-limited public APIs** that require a solved challenge before an expensive request.

## Dependencies for Cap Hosting

### Deployment Dependencies

- Cap `tiago2/cap:3.1.14` (public)
- Valkey `valkey/valkey:9.0.6-alpine`
- [Cap](https://trycap.dev), by Tiago Rangel and contributors, Apache-2.0. This template only configures the published image.

### Implementation Details

- **`PORT=3000`.** Cap listens on 3000 and does not read `PORT`. Without it, Railway probes the health check on another port and fails the deploy after five minutes, with Cap running fine the whole time. This is why the common Cap template fails four deploys in ten.
- **The health check asks Valkey.** `/health` answers 200 only when Valkey replies to `PING`, so a deploy that cannot reach its database does not go live.
- **The data volume is writable.** The image runs as the unprivileged `bun` user, and Railway mounts volumes owned by root, so Cap could not save the IP geolocation database it downloads into `/usr/src/app/data`. `RAILWAY_RUN_UID=0` fixes the permissions.
- **Valkey has a password**, generated per deployment, and writes snapshots to its volume.
- **Rate limiting sees the real client IP.** `RATELIMIT_IP_HEADER=x-forwarded-for`: Railway's edge replaces any `X-Forwarded-For` a client sends with the real address, so the first entry cannot be forged to dodge the limit. Checked by sending a forged header through Railway.

### Configuration

`ADMIN_KEY` and the Valkey password are generated. Find `ADMIN_KEY` in the Cap service's Variables tab; it is the dashboard password, so keep it private.

### Verification

Deployed from this template into an empty project, both services starting at once: Cap was live in under a minute, `/health` returned `ok` (Cap reached Valkey with its password) and the dashboard answered 200.

Locally, on the same images: admin sign-in, creating a site key, issuing a challenge for it, and the key surviving a restart of both Cap and Valkey. Cap used about 30 MB of memory, well inside the Free plan. The volume fix was checked on a root-owned volume: as `bun`, writing to it is refused; with UID 0 it works.

## Why Deploy Cap on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Cap on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.


## Similar templates

- [Keycloak](https://railway.com/deploy/mSwigX) — Keycloak template with keywind theme + apple and discord providers
- [lua-protector](https://railway.com/deploy/lua-protector) — Test deployed my project first
- [bknd](https://railway.com/deploy/p4nTYL) — Feature-rich yet lightweight backend

Open this page in a browser: https://railway.com/deploy/cap-captcha-or-self-hosted-proof-of-work
