Self-Hosted Deployment

Deploy runpiper on your own infrastructure using Docker.

Prerequisites

Before you begin, make sure you have:

  1. Docker installed and running

    Check if Docker is running:

    docker ps
    
  2. PostgreSQL database

    runpiper uses PostgreSQL for data persistence.

Quick Start

The fastest way to run a self-hosted instance:

# 1. Clone the repository
git clone https://codeberg.org/runpiper/runpiper.git
cd runpiper

# 2. Start PostgreSQL
docker compose up -d db

# 3. Create configuration
cp config.toml.example config.toml
# Edit config.toml to set DATABASE_URL

# 4. Create environment file
cp .env.example .env
# Edit .env to set required variables

# 5. Run runpiper
docker run -p 8080:8080 \
  -v ./config.toml:/app/config.toml \
  --env-file .env \
  codeberg.org/runpiper/runpiper:latest

# 6. Bootstrap the instance (in another terminal)
rp init

Configuration

config.toml

Create a configuration file from the example:

cp config.toml.example config.toml

Edit the file to configure your instance:

# Database connection (use environment variable)
database_url = "env:DATABASE_URL"

# Server settings
[server]
host = "0.0.0.0"
port = 8080

# Admin key (use environment variable)
admin_key = "env:runpiper_ADMIN_KEY"

.env

Create an environment file:

cp .env.example .env

Set the required variables:

# Database connection
DATABASE_URL=postgresql://runpiper:runpiper@host.docker.internal:5432/runpiper

# Admin key for bootstrapping
runpiper_ADMIN_KEY=your-secret-admin-key-here

# Config file path
runpiper_CONFIG=/app/config.toml

Running with Docker

Using Docker Compose (Database)

Start the PostgreSQL database:

docker compose up -d db

Wait a few seconds for the database to be ready.

Using Docker Run (Application)

Verify config file exists and is a file (not a directory):

ls -la config.toml
# Should show: -rw-r--r-- ... config.toml
# NOT: drwxr-xr-x ... config.toml

If it’s a directory, remove it first:

rm -rf config.toml && cp config.toml.example config.toml

Run the container:

docker run -p 8080:8080 \
  -v ./config.toml:/app/config.toml \
  --env-file .env \
  codeberg.org/runpiper/runpiper:latest

Your runpiper instance will be available at http://localhost:8080.

Docker Network Configuration

By default, the setup uses host.docker.internal to connect to the database. If you’re running both containers in the same Docker network:

  1. Update DATABASE_URL in .env:

    DATABASE_URL=postgresql://runpiper:runpiper@runpiper-db:5432/runpiper
    
  2. Add --network <network-name> to the docker run command.

Bootstrapping Your Instance

After your server is running, you need to bootstrap it to create the first admin user.

1. Set the Admin Key

Make sure runpiper_ADMIN_KEY is set in your .env file or exported:

export runpiper_ADMIN_KEY=your-secret-admin-key-here

2. Bootstrap the Instance

rp init

You’ll be prompted for:

  • Admin key (the runpiper_ADMIN_KEY from your config)
  • Server endpoint (e.g., http://localhost:8080)
  • Admin email address
  • Organization name

3. Create Invite Codes

After bootstrapping, you’ll be automatically logged in. Create invite codes for other users:

rp invite create user@example.com
rp invite create user2@example.com --expires-days 30

Users can redeem invite codes by running:

rp auth login

and entering their invite code.

Managing Your Instance

Start the Server

rp serve

Create Users

As the admin, create invite codes:

rp invite create user@example.com

List all invite codes:

rp invite list

Revoke an invite:

rp invite revoke user@example.com

Production Deployment

Security Considerations

  1. Use strong admin keys

    runpiper_ADMIN_KEY=$(openssl rand -hex 32)
    
  2. Use environment variables for secrets

    • Never hardcode credentials in config.toml
    • Use .env files that are not committed to git
  3. Enable HTTPS

    • Use a reverse proxy like nginx or Caddy
    • Obtain SSL certificates via Let’s Encrypt
  4. Database security

    • Use strong database passwords
    • Restrict database access to the application only
  5. Firewall rules

    • Only expose necessary ports
    • Use a VPN or bastion host for admin access

Reverse Proxy Example (nginx)

server {
    listen 443 ssl http2;
    server_name runpiper.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/runpiper.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/runpiper.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8080;
        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;
    }
}

Monitoring

Consider adding:

  • Logs - Configure logging to a file or centralized service
  • Metrics - Add Prometheus metrics export
  • Health checks - Implement /health endpoint monitoring

Troubleshooting

“Cannot connect to the Docker daemon”

Start Docker Desktop or your Docker service.

“Config read error: No such file or directory”

If config.toml doesn’t exist:

cp config.toml.example config.toml

If config.toml is a directory:

ls -la config.toml
# If it shows drwxr-xr-x:
rm -rf config.toml
cp config.toml.example config.toml

“database connection failed: error with configuration: relative URL without a base”

The DATABASE_URL environment variable is not set or is empty:

  1. Start the database first: docker compose up -d db
  2. Set DATABASE_URL in your .env file
  3. Use --env-file .env in your Docker command

“admin_key not configured”

The server needs the runpiper_ADMIN_KEY environment variable set:

  1. Set it in your .env file
  2. Use --env-file .env in your Docker command
  3. When running with rp serve, export it: export runpiper_ADMIN_KEY=your-secret-key

“Can’t login locally without an invite code”

You need to bootstrap the instance first with rp init. This creates the first admin user without requiring an invite code.

Database connection issues

If the application can’t connect to the database:

  1. Check the database is running: docker compose ps db
  2. Check database logs: docker compose logs db
  3. Verify DATABASE_URL matches database credentials
  4. For Docker networking, ensure both containers are on the same network

Next Steps