Skip to content

Self-hosted

Run BenchPress on your own server.

One install, on one Linux box, for the whole team. Every environment is a Docker container on your host, reachable on your own WireGuard network. No account, no telemetry, no ceiling on labs, and no bill — it is AGPL-3.0.

Still comparing? BenchPress and frappe_docker

Read this first

BenchPress is alpha. A lab user holds root inside their own bench, and without a user-namespace boundary that is root on the host — so setup.sh checks for sysbox-runc or Docker userns-remap before anything else. Put this on a development box or a VM you can rebuild. Never on a workstation, and never on a host that carries production.

What you need before you start

Six things, and none of them can be fixed from inside the app. Each one blocks the install or the first deploy.

  • A Linux host

    The setup script uses apt and sysctl. There is no macOS or Windows build, and a laptop is the wrong machine for it.

  • A Frappe v16 bench

    BenchPress installs into a bench you already run and drives that host's Docker. It is not a standalone service. Python 3.14+, Node 24, Docker 20+.

  • A container privilege boundary

    sysbox-runc registered with Docker, or userns-remap. Without one, root inside a bench is root on the host.

  • IP forwarding on

    net.ipv4.ip_forward = 1, or the kernel drops tunnel traffic on its way to a bench and the deploy fails on the network step.

  • 44556/UDP open

    On ufw and on any cloud firewall or security group in front of the host. ufw cannot see that layer, and a WireGuard peer never handshakes without it.

  • A domain you control

    Sites are addressed as <instance>.<base domain>, and base_domain is the one required field on the settings form.

The preconditions and the measured sizing, in full

Size the host before you buy it

Disk is the constraint, not CPU. These are measured on the host that runs this site, not estimated — a 20 GB or 40 GB VPS root disk fails in the middle of an image build, which is the worst place to find out.

  • 100 GB Free disk for one lab image, with room to rebuild it
  • 250 GB For a catalog the size of this host's — twelve images, 54.74 GB
  • 5.5-19.7 GB Per lab image, smallest to largest measured
  • 1 core Per bench, the enforced floor. Measured need was half that

The install, in six steps

Two of them are commands. The guide has all six, with the output each one prints and the error it raises when a precondition is missing.

  1. Install both apps into the bench

    There are two repositories and you clone both. BenchPress names vpn_management in required_apps, but bench resolves a bare name under the frappe and erpnext accounts only — so it will not find it for you.

    cd /path/to/your/frappe-bench
    bench get-app https://github.com/Venkateshvenki404224/vpn_management --branch version-16
    bench get-app https://github.com/Venkateshvenki404224/benchpress --branch version-16
    bench pip install docker
    bench --site <site> install-app benchpress
    bench --site <site> migrate
  2. Run the setup script

    It is idempotent, so a second run reports what is already correct and changes nothing. Four steps: the docker group, the privilege boundary, the shared MariaDB and Redis containers, and IP forwarding. --strict exits rather than warning when there is no boundary.

    bash apps/benchpress/setup.sh <site> --strict
  3. Finish in the guide

    Build the frontend, open 44556/UDP, set your base domain in Settings, and open the dashboard. Then run the app's own diagnostics — twelve read-only checks that ask Docker, MariaDB and the kernel what is true instead of reading the config.

What happens after it is running

  • The first screen is the Overview

    How many environments are running, stopped or broken, the average deploy time over the last seven days, and twelve infrastructure checks. Seven days is not a setting: deploy logs are cleared on that schedule.

  • The first deploy builds an image

    An uncached template takes tens of minutes and several gigabytes the first time. Every deploy after it restores from that image — 51 seconds on average across 50 runs on the host that serves this page.

  • A teammate gets a key, not a port

    They register a device, import the WireGuard config, and reach the site and the browser VS Code session over the tunnel. One key per device, revocable in a click. Nothing is published to the internet.

What breaks, and what it means

The six failures a first install actually hits. Every one of them has a named cause, which is the point of writing them down.

install-app fails with an empty InvalidRemoteException
vpn_management was never cloned. Frappe looked for it under the frappe and erpnext accounts, gave up there, and never reported a missing app. Run the first get-app, then install-app again.
The dashboard is blank or unstyled
The frontend was never built. Build it, then clear the site cache.
Settings will not save
base_domain is empty, and it is required. Sites are addressed under it.
Every deploy fails on a Docker call
The bench started before the docker group change took effect. Log out, log back in, restart the bench.
setup.sh warns that there is no privilege boundary
Neither sysbox-runc nor userns-remap is present, so container root is host root. Read Production safety before running anything you care about.
A build dies partway through
The disk filled. A build holds the new image while its base layers are still there, so the floor is higher than one image.

Every symptom, cause and fix

Or have us do it.

The install is documented because it should be possible without us. When you would rather it were done in an afternoon by someone who has done it before, that is a fixed-scope engagement — and the hosted build is the same repository with billing attached, if you would rather not run a server at all.