Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AZhur771/pivpn-web/llms.txt

Use this file to discover all available pages before exploring further.

PiVPN Web includes a built-in scheduler that enforces per-client traffic limits automatically. Every minute, the scheduler inspects the current transfer statistics for every WireGuard client and disables any client that has consumed more than its allocated share. Banned clients are re-enabled automatically after 24 hours without any manual intervention.

How the scheduler runs

The scheduler is registered as a cron job inside the Express server using the pattern */1 * * * * — meaning it fires once per minute. Each invocation acquires an exclusive lock (client-check) before running so that concurrent executions cannot race against each other.
// From lib/Server.ts
const EVERY_10_MINUTES_CRON = '*/1 * * * *';

cron.schedule(EVERY_10_MINUTES_CRON, () => {
  this.lock.acquireAndExecute({ name: 'client-check' }, async () => {
    scheduler.invoke().catch(console.error);
  });
});

How limits are calculated

The scheduler uses two hardcoded constants defined in lib/Scheduler.ts:
private readonly TRAFFIC_LIMIT = 161061273600; // 150 GB
private readonly LIMIT_MULTIPLIER = 5;
The per-client limit is calculated as:
limitPerClient = Math.round((TRAFFIC_LIMIT / clientCount) * LIMIT_MULTIPLIER)
Breaking this down:
  • 150 GB pool — the total assumed bandwidth budget shared across all clients.
  • Fair share — the pool is divided equally by the number of clients known to WireGuard.
  • 5× multiplier — each client is permitted to use five times its fair share before being banned. This prevents a single heavy user from being banned on a lightly loaded server.
Example: With 10 clients the per-client limit is:
round((161,061,273,600 / 10) * 5) = 80,530,636,800 bytes ≈ 75 GB
The 150 GB pool (161,061,273,600 bytes) and the 5 multiplier are currently hardcoded in the source code and cannot be changed via environment variables. To use different thresholds you would need to rebuild the image from source.

Ban behavior

When a client’s combined download and upload (transferRx + transferTx) exceeds limitPerClient, the scheduler:
  1. Saves a BannedClient record to the SQLite database with bannedTill set to now + 1 day.
  2. Calls pivpn off <name> via SSH to disable the client on the WireGuard interface.
On each subsequent run, the scheduler also checks every existing ban record. If the current time is after bannedTill, the record is deleted and the client is re-enabled automatically via pivpn on <name>.

BannedClient entity

// lib/entities/BannedClient.ts
@Entity()
export class BannedClient {
  @PrimaryColumn()
  publicKey!: string;       // WireGuard public key — unique identifier for the client

  @Column()
  bannedTill!: Date;        // Timestamp after which the ban is lifted (now + 1 day)

  @Column()
  totalDownloaded!: number; // Total bytes (rx + tx) at the time of banning
}

Dashboard indicator

The PiVPN Web dashboard displays the current per-client limit so you can see at a glance how much traffic each client is permitted. Clients that are approaching or have exceeded the limit are highlighted in red in the UI. Clients that have been automatically disabled by the scheduler are shown as disabled in the client list.
Traffic statistics come from wg show all dump, which reports cumulative byte counters since the WireGuard interface was last started. If the WireGuard interface is restarted (for example, after a reboot or a wg-quick down/up cycle), all counters reset to zero and previously close-to-limit clients effectively get a fresh allowance.

Build docs developers (and LLMs) love