Skip to content

Local Development ​

Run a complete Hoppla instance on a laptop — no Kubernetes, no cluster — so your engineers can build integrations, test white-label branding, or explore the product. It uses the same prebuilt images you deploy to production (nothing is built from source), fronted by a small reverse proxy so the dashboard, API, session cookies, and realtime updates all work on one http://localhost origin.

This is a development instance

It runs with a single database role (so PostgreSQL row-level security is bypassed locally — application-level tenant scoping still applies), relaxes captcha and secure-cookie requirements for http://localhost, and ships a shared throwaway APP_KEY. Never use it in production. For production, use install.sh and the Helm chart.

What you need ​

  • Docker with Compose v2 (docker compose version)
  • Your registry credentials and a development license file from your welcome pack

The bundle lives in the distribution repo under dist/selfhosted/local/. It contains a docker-compose.yml, a ready-to-use .env.example, a config.js, and a Caddyfile.

Get the development license ​

The development license is issued from the Hoppla admin console like any other, with one difference: its environment is development, which is domain-unbound — it carries no bound domains and produces no binding warning, so it runs on any host your engineers use (localhost, a shared dev box, CI). Ask your Hoppla contact to include a development license in your welcome pack (a single pack can bundle production, staging, testing, and development licenses together).

Start it ​

bash
cd dist/selfhosted/local

# 1. Configuration — the defaults work as-is for local dev.
cp .env.example .env

# 2. Authenticate Docker to the registry (once), using the classic
#    read:packages token from your welcome pack.
echo "<YOUR_GHCR_TOKEN>" | docker login ghcr.io -u "<YOUR_GITHUB_USERNAME>" --password-stdin

# 3. Drop in your development license.
cp /path/to/hoppla.license ./license/hoppla.license

# 4. Bring the stack up (pulls the images on first run).
docker compose up -d

# 5. Create the first admin + organization (idempotent).
docker compose exec api php artisan hoppla:install

Open http://localhost and log in. Outbound mail is captured by Mailpit at http://localhost:8025 — no real email leaves the machine.

Verify the license and instance health at any time:

bash
docker compose exec api php artisan license:inspect
docker compose exec api php artisan hoppla:doctor

Injecting the license ​

The application reads its license from one of two places (see Configuration):

  • A file — HOPPLA_LICENSE_PATH (default /var/hoppla/license/hoppla.license). The local bundle mounts ./license/hoppla.license there for you.
  • An inline value — HOPPLA_LICENSE set to the full armored license text (the -----BEGIN HOPPLA LICENSE----- … -----END… block). Useful when a file mount is awkward, e.g. CI or a hosted dev environment.

If you set HOPPLA_LICENSE, you can skip the file mount entirely.

Previewing your rebrand ​

White-label branding — brand name, colors, logo, login page, portal domains — is configured inside the app once you're logged in, and it renders on this local instance exactly as it would in production. That makes local the right place to build and preview a reseller rebrand before shipping it. See White-Label for the full walkthrough.

Updating ​

Bump HOPPLA_VERSION in .env (check VERSIONS.md in the distribution repo for the latest), then:

bash
docker compose pull
docker compose up -d   # schema migrations run automatically on the way up

Troubleshooting ​

SymptomFix
Images won't pull (denied / unauthorized)Re-run docker login ghcr.io … with a classic read:packages token — fine-grained tokens don't authenticate to GHCR.
http://localhost shows nothing / port 80 in useSet HTTP_PORT in .env to a free port (e.g. 8080) and open that. Live (websocket) updates assume the standard port; everything else works on any port.
"No license configured"Confirm ./license/hoppla.license exists, then docker compose restart api worker scheduler.
Login rejected / captcha errorEnsure .env keeps APP_ENV=local and TURNSTILE_REQUIRED=false (the defaults).
Checks never runThe scheduler and worker services must be up (docker compose ps) — they need the license mounted too.

To wipe everything and start fresh: docker compose down -v (deletes the database volume).