Self-Hosting
Run your own shieldcn badge engine with Docker.
Run your own shieldcn badge engine — no reliance on shieldcn.dev infrastructure.
Quick Start
Create a docker-compose.yml file:
services:
engine:
image: ghcr.io/jal-co/shieldcn/engine:latest
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://shieldcn:shieldcn@postgres:5432/shieldcn
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:17-alpine
environment:
POSTGRES_USER: shieldcn
POSTGRES_PASSWORD: shieldcn
POSTGRES_DB: shieldcn
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U shieldcn"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
pgdata:
Then run:
docker compose up -d
# Verify it's working
curl http://localhost:3000/api/health/
curl http://localhost:3000/badge/self--hosted-green.svg
Badges are served at http://localhost:3000. Use them the same way as shieldcn.dev — just swap the domain:


All query parameters work — variant, size, mode, theme, logo, color, gradient, etc.
Environment Variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | ✅ | PostgreSQL connection string |
GITHUB_TOKEN | — | Single GitHub token fallback (5,000 req/hr) |
GITHUB_OAUTH_CLIENT_ID | — | OAuth app for token pool |
GITHUB_OAUTH_CLIENT_SECRET | — | OAuth app secret |
YOUTUBE_API_KEY | — | YouTube Data API key |
UPSTASH_REDIS_REST_URL | — | Upstash Redis for persistent cache |
UPSTASH_REDIS_REST_TOKEN | — | Upstash Redis token |
NEXT_PUBLIC_URL | — | Engine base URL (default: http://localhost:3000) |
GitHub Badges
GITHUB_TOKEN, GitHub badges are limited to 60 requests/hour. Add a token to raise this to 5,000/hour.GitHub badges hit the GitHub API which has a rate limit of 60 requests/hour without authentication. To raise this to 5,000/hour, add a GITHUB_TOKEN to your environment:
services:
engine:
image: ghcr.io/jal-co/shieldcn/engine:latest
environment:
- DATABASE_URL=postgresql://shieldcn:shieldcn@postgres:5432/shieldcn
- GITHUB_TOKEN=ghp_your_token_here
Create a token at github.com/settings/tokens — no scopes needed (public data only).
For higher throughput, set up the token pool to distribute requests across multiple tokens.
What's Included
The self-hosted engine serves all badge endpoints:
- All 30+ badge providers (npm, GitHub, PyPI, Docker Hub, etc.)
- SVG, PNG, and JSON output formats
- All variants, themes, gradients, and customization options
- GitHub token pool with OAuth flow
- Memo badges (persistent badges with Bearer auth)
- Health check at
/api/health/
What's Not Included
The engine does not include:
- Documentation site
- Gallery, showcase, or landing pages
- Analytics (OpenPanel)
- shadcn component registry
These are only in the packages/web site at shieldcn.dev.
Token Pool Setup
The token pool distributes GitHub API requests across multiple OAuth tokens to stay under rate limits.
Option 1: Single Token (Simple)
Set GITHUB_TOKEN to a personal access token with no scopes (public data only).
Option 2: OAuth Token Pool (Scalable)
- Create a GitHub OAuth App at github.com/settings/developers
- Set the callback URL to
{NEXT_PUBLIC_URL}/api/auth/github/callback - Set
GITHUB_OAUTH_CLIENT_IDandGITHUB_OAUTH_CLIENT_SECRET - Users can authorize at
{NEXT_PUBLIC_URL}/api/auth/github
Reverse Proxy
If you want to serve badges from a custom domain:
Nginx
server {
listen 80;
server_name badges.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Caddy
badges.example.com {
reverse_proxy localhost:3000
}
Upgrading
docker compose pull
docker compose up -d
Data Source
The engine connects to the same upstream APIs as shieldcn.dev — npm registry, GitHub API, Docker Hub, etc. No data is proxied through shieldcn.dev.