title: Getting Started with Charon description: Get your first website up and running in minutes. A beginner-friendly guide to setting up Charon reverse proxy.

Getting Started with Charon

Welcome! Let's get your first website up and running. No experience needed.


What Is This?

Imagine you have several apps running on your computer. Maybe a blog, a file storage app, and a chat server.

The problem: Each app is stuck on a weird address like 192.168.1.50:3000. Nobody wants to type that.

Charon's solution: You tell Charon "when someone visits myblog.com, send them to that app." Charon handles everything elseβ€”including the green lock icon (HTTPS) that makes browsers happy.


Step 1: Install Charon

Option A: Docker Compose (Easiest)

Create a file called docker-compose.yml:

services:
  charon:
    # Docker Hub (recommended)
    image: wikid82/charon:latest
    # Alternative: GitHub Container Registry
    # image: ghcr.io/wikid82/charon:latest
    container_name: charon
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - ./charon-data:/app/data
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - CHARON_ENV=production

Then run:

docker-compose up -d

Option B: Docker Run (One Command)

Docker Hub (recommended):

docker run -d \
  --name charon \
  -p 80:80 \
  -p 443:443 \
  -p 8080:8080 \
  -v ./charon-data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -e CHARON_ENV=production \
  wikid82/charon:latest

Alternative (GitHub Container Registry):

docker run -d \
  --name charon \
  -p 80:80 \
  -p 443:443 \
  -p 8080:8080 \
  -v ./charon-data:/app/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -e CHARON_ENV=production \
  ghcr.io/wikid82/charon:latest

What Just Happened?

Open http://localhost:8080 in your browser!


Step 1.5: Database Migrations (If Upgrading)

If you're upgrading from a previous version and using a persistent database, you may need to run migrations to ensure all security features work correctly.

When to Run Migrations

Run the migration command if:

Skip this step if:

How to Run Migrations

Docker Compose:

docker exec charon /app/charon migrate

Docker Run:

docker exec charon /app/charon migrate

Expected Output:

{"level":"info","msg":"Running database migrations for security tables...","time":"..."}
{"level":"info","msg":"Migration completed successfully","time":"..."}

What This Does:

After Migration:

If you enabled CrowdSec before the migration, restart the container:

docker restart charon

Auto-Start Behavior:

CrowdSec will automatically start if it was previously enabled. The reconciliation function runs at startup and checks:

  1. SecurityConfig table for crowdsec_mode = "local"

Step 1.8: Emergency Token Configuration (Development & E2E Tests)

The emergency token is a security feature that allows bypassing all security modules in emergency situations (e.g., lockout scenarios). It is required for E2E test execution and recommended for development environments.

Purpose

Generation

Choose your platform:

Linux/macOS (recommended):

openssl rand -hex 32

Windows PowerShell:

[Convert]::ToBase64String([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32))

Node.js (all platforms):

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Local Development

Add to .env file in project root:

CHARON_EMERGENCY_TOKEN=<paste_64_character_token_here>

Example:

CHARON_EMERGENCY_TOKEN=7b3b8a36a6fad839f1b3122131ed4b1f05453118a91b53346482415796e740e2

Verify:

# Token should be exactly 64 characters
echo -n "$(grep CHARON_EMERGENCY_TOKEN .env | cut -d= -f2)" | wc -c

CI/CD (GitHub Actions)

For continuous integration, store the token in GitHub Secrets:

  1. Navigate to: Repository Settings β†’ Secrets and Variables β†’ Actions
  2. Click "New repository secret"
  3. Name: CHARON_EMERGENCY_TOKEN
  4. Value: Generate with one of the methods above
  5. Click "Add secret"

πŸ“– Detailed Instructions: See GitHub Setup Guide

Rotation Schedule

Security Best Practices

βœ… DO:

❌ DON'T:


  1. Settings table for security.crowdsec.enabled = "true"
  2. Starts CrowdSec if either condition is true

How it works:

You'll see this in the logs:

{"level":"info","msg":"CrowdSec reconciliation: starting startup check"}
{"level":"info","msg":"CrowdSec reconciliation: starting based on SecurityConfig mode='local'"}
{"level":"info","msg":"CrowdSec reconciliation: successfully started and verified CrowdSec","pid":123}

Verification:

# Wait 15 seconds for LAPI to initialize
sleep 15

# Check if CrowdSec auto-started
docker exec charon cscli lapi status

Expected output:

βœ“ You can successfully interact with Local API (LAPI)

Troubleshooting:

If CrowdSec doesn't auto-start:

  1. Check reconciliation logs:

    docker logs charon 2>&1 | grep "CrowdSec reconciliation"
    
  2. Verify SecurityConfig mode:

    docker exec charon sqlite3 /app/data/charon.db \
      "SELECT crowdsec_mode FROM security_configs LIMIT 1;"
    

    Expected: local

  3. Check directory permissions:

    docker exec charon ls -la /var/lib/crowdsec/data/
    

    Expected: charon:charon ownership

  4. Manual start:

    curl -X POST http://localhost:8080/api/v1/admin/crowdsec/start
    

For detailed troubleshooting: See CrowdSec Startup Fix Documentation


Step 2: Configure Application URL (Recommended)

Before inviting users, you should configure your Application URL. This ensures invite links work correctly from external networks.

What it does: Sets the public URL used in user invitation emails and links.

When you need it: If you plan to invite users or access Charon from external networks.

How to configure:

  1. Go to System Settings (gear icon in sidebar)
  2. Scroll to "Application URL" section
  3. Enter your public URL (e.g., https://charon.example.com)
    • Must start with http:// or https://
    • Should be the URL users use to access Charon
    • No path components (e.g., /admin)
  4. Click "Validate" to check the format
  5. Click "Test" to verify the URL opens in a new tab
  6. Click "Save Changes"

What happens if you skip this? User invitation emails will use the server's local address (like http://localhost:8080), which won't work from external networks. You'll see a warning when previewing invite links.

Examples:


Step 3: Add Your First Website

Let's say you have an app running at 192.168.1.100:3000 and you want it available at myapp.example.com.

  1. Click "Proxy Hosts" in the sidebar
  2. Click the "+ Add" button
  3. Fill in the form:
    • Domain: myapp.example.com
    • Forward To: 192.168.1.100
    • Port: 3000
    • Scheme: http (or https if your app already has SSL)
    • Enable Standard Proxy Headers: βœ… (recommended β€” allows your app to see the real client IP)
  4. Click "Save"

Done! When someone visits myapp.example.com, they'll see your app.

What Are Standard Proxy Headers?

By default (and recommended), Charon adds special headers to requests so your app knows:

When to disable: Only turn this off for legacy applications that don't understand these headers.

Learn more: See Standard Proxy Headers in the features guide.


Step 4: Get HTTPS (The Green Lock)

For this to work, you need:

  1. A real domain name (like example.com) pointed at your server
  2. Ports 80 and 443 open in your firewall

If you have both, Charon will automatically:

You don't do anything. It just works.

By default, Charon uses "Auto" mode, which tries Let's Encrypt first and automatically falls back to ZeroSSL if needed. You can change this in System Settings if you want to use a specific certificate provider.

Testing without a domain? See Testing SSL Certificates for a practice mode.


Common Questions

"Where do I get a domain name?"

You buy one from places like:

Cost: Usually $10-15/year.

"How do I point my domain at my server?"

In your domain provider's control panel:

  1. Find "DNS Settings" or "Domain Management"
  2. Create an "A Record"
  3. Set it to your server's IP address

Wait 5-10 minutes for it to update.

"Can I change which certificate provider is used?"

Yes! Go to System Settings and look for the SSL Provider dropdown. The default "Auto" mode works best for most users, but you can choose a specific provider if needed. See Features for details.

"Can I use this for apps on different computers?"

Yes! Just use the other computer's IP address in the "Forward To" field.

If you're using Tailscale or another VPN, use the VPN IP.

"Will this work with Docker containers?"

Absolutely. Charon can even detect them automatically:

  1. Click "Proxy Hosts"
  2. Click "Docker" tab
  3. You'll see all your running containers
  4. Click one to auto-fill the form

Common Development Warnings

Expected Browser Console Warnings

When developing locally, you may encounter these browser warnings. They are normal and safe to ignore in development mode:

COOP Warning on HTTP Non-Localhost IPs

Cross-Origin-Opener-Policy policy would block the window.closed call.

When you'll see this:

Why it appears:

What to do: Nothing! This is expected behavior. The warning disappears when you deploy to production with HTTPS.

Learn more: See COOP Behavior in the security documentation.

401 Errors During Authentication Checks

GET /api/auth/me β†’ 401 Unauthorized

When you'll see this:

Why it appears:

What to do: Nothing! This is normal application behavior. Once you log in, these errors stop appearing.

Learn more: See Authentication Flow for details on how Charon validates user sessions.

Development Mode Behavior

Features that behave differently in development:

Production mode automatically enables full security when accessed over HTTPS.


What's Next?

Now that you have the basics:


Staying Updated

Security Update Notifications

To receive notifications about security updates:

1. GitHub Watch

Click "Watch" β†’ "Custom" β†’ Select "Security advisories" on the Charon repository

2. Automatic Updates with Watchtower

services:
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_POLL_INTERVAL=86400  # Check daily

3. Diun (Docker Image Update Notifier)

For notification-only (no auto-update), use Diun. This sends alerts when new images are available without automatically updating.

Best Practices:


Stuck?

Ask for help β€” The community is friendly!

Maintainers: History-rewrite Tools

If you are a repository maintainer and need to run the history-rewrite utilities, find the scripts in scripts/history-rewrite/.

Minimum required tools:

Quick checks before running scripts:

# Fetch full history (non-shallow)
git fetch --unshallow || true
command -v git || (echo "install git" && exit 1)
command -v git-filter-repo || (echo "install git-filter-repo" && exit 1)
command -v pre-commit || (echo "install pre-commit" && exit 1)

See docs/plans/history_rewrite.md for the full checklist, usage examples, and recovery steps.