NAME
pagi-server - PAGI Reference Server CLI
SYNOPSIS
pagi-server [options] [app] [key=value ...]
# Serve current directory (default)
pagi-server
# Serve a specific directory
pagi-server PAGI::App::Directory root=/var/www
# Run a PAGI app file
pagi-server ./app.pl
pagi-server --app app.pl # legacy syntax
# With options
pagi-server -p 8080 PAGI::App::Directory root=/var/www
pagi-server -w 4 ./myapp.pl
DESCRIPTION
pagi-server is the command-line interface for running PAGI applications. It can load apps from files or from installed PAGI::App::* modules.
OPTIONS
- --lib, -I PATH
-
Add PATH to
@INCbefore loading the app. Can be specified multiple times to add multiple paths, similar toperl -I.pagi-server -I ./lib -I ./vendor/lib ./app.pl - --app, -a FILE
-
Path to .pl/.psgi file returning PAGI app coderef. Legacy option for backward compatibility; you can also just pass the file as an argument.
- --host, -h HOST
-
Bind address (default: 127.0.0.1, localhost only)
The default only accepts connections from localhost, which is secure by default for development. For headless servers or production deployments where remote clients need to connect:
# Bind to all IPv4 interfaces pagi-server --host 0.0.0.0 ./app.pl # Bind to specific interface pagi-server --host 192.168.1.100 ./app.pl - --port, -p PORT
-
Bind port (default: 5000)
- --workers, -w NUM
-
Number of worker processes (default: 1)
- --max-requests NUM
-
Maximum number of requests a worker process will handle before restarting. After serving this many requests, the worker gracefully shuts down and the parent spawns a replacement.
Default: 0 (disabled - workers run indefinitely)
When to use: Long-running deployments where gradual memory growth is a concern, or applications with known memory leaks.
Note: Only applies in multi-worker mode (
--workers> 1). In single-worker mode, this setting is ignored. - --max-connections NUM
-
Maximum number of concurrent connections per worker before returning HTTP 503 Service Unavailable. This prevents "Too many open files" crashes.
Default: 0 (auto-detect from ulimit - 50)
Behavior: When at capacity, new connections receive a 503 response with a
Retry-After: 5header. The TCP backlog queues additional connections.When to adjust:
Getting 503s under load? Increase ulimit or set higher limit
Want predictable capacity? Set explicit limit matching your ulimit
Memory constrained? Lower the limit to reduce concurrent request memory
Note: In multi-worker mode, this limit applies per-worker. With
--workers 4 --max-connections 100, total capacity is 400 connections. - --max-body-size NUM
-
Maximum request body size in bytes. Requests exceeding this limit receive HTTP 413 (Payload Too Large).
Default: 10,000,000 (10MB)
Special value: 0 means unlimited (not recommended for public servers).
When to adjust:
File uploads? Increase to 50-100MB as needed
API with large JSON? Increase to match expected payload sizes
Behind reverse proxy? Can set to 0 if proxy enforces its own limit
- --log-level LEVEL
-
Set the logging verbosity level. Messages below this level are suppressed.
Levels: debug, info, warn, error
Default: info
Production Options
- -D, --daemonize
-
Fork to background and run as a daemon. Errors during startup (including binding to port) are reported before daemonizing.
- --pid FILE
-
Write the process ID to FILE. The file is removed on clean shutdown. Useful for init scripts:
kill $(cat /var/run/pagi.pid) - --user USER
-
After binding to the port, drop privileges to run as USER. Requires starting the server as root. Commonly used with privileged ports (80, 443).
- --group GROUP
-
After binding to the port, drop privileges to run as GROUP. If not specified but --user is, uses the user's primary group.
- --listener_backlog -b
-
Listener queue size. No default, if left blank then the server sets a default that is rational for itself.
- --timeout
-
Seconds before we timeout the request. If left blank will default to whatever is default for the server.
- --loop, -l BACKEND
-
Event loop backend (Poll, EV, Epoll, UV). Default is auto-detect.
- --ssl-cert FILE
-
Path to SSL certificate (enables HTTPS)
- --ssl-key FILE
-
Path to SSL private key
- --access-log FILE
-
Path to access log file (default: STDERR)
- --no-access-log
-
Disable access logging entirely. Eliminates per-request I/O overhead, improving throughput by 5-15% depending on workload. Useful for benchmarking or when access logs are handled by a reverse proxy.
- --reuseport
-
Enable SO_REUSEPORT mode for multi-worker servers. Each worker creates its own listening socket with SO_REUSEPORT.
Linux 3.9+: The kernel load-balances incoming connections across workers, reducing accept() contention and improving p99 latency under high concurrency.
macOS/BSD: SO_REUSEPORT allows binding but does NOT provide kernel load balancing. May actually decrease performance - benchmark before using.
- --max-receive-queue NUM
-
Maximum number of messages in the WebSocket receive queue before the connection is closed with code 1008 (Policy Violation). This is a DoS protection mechanism to prevent memory exhaustion from slow consumers.
Default: 1000 messages
Unit: Message count, not bytes. Each WebSocket frame counts as one message.
Tuning: Lower to 100-500 for slow message processing. Raise to 5000-10000 for trusted high-throughput clients. Memory risk per connection is approximately max_receive_queue × average_message_size.
- --max-ws-frame-size NUM
-
Maximum size in bytes for a single WebSocket frame payload. When a client sends a frame larger than this limit, the connection is closed.
Default: 65536 bytes (64KB)
Unit: Bytes (not messages). This controls individual frame size.
Tuning: For chat/control messages, default 64KB is sufficient. For file uploads via WebSocket, increase to 1-16MB. Memory impact: each connection can buffer up to this amount during frame parsing.
- --quiet, -q
-
Suppress startup banner
- --help
-
Show this help
Signal Handling
- TERM, INT
-
Graceful shutdown. Stops accepting new connections, waits for existing requests to complete, then exits.
- HUP
-
Graceful restart (multi-worker mode only). Terminates all current workers and spawns fresh replacements. Useful for config/code changes.
- TTIN
-
Increase worker count by 1 (multi-worker mode only).
- TTOU
-
Decrease worker count by 1 (multi-worker mode only). Maintains at least 1 worker.
APP SPECIFICATION
The app can be specified in three ways:
Module Name
If the argument contains ::, it's treated as a Perl module name:
pagi-server PAGI::App::Directory root=/var/www
The module must have new() and to_app() methods. Constructor arguments are passed as key=value pairs.
File Path
If the argument contains / or ends with .pl/.psgi:
pagi-server ./app.pl
pagi-server /path/to/myapp.psgi
The file must return a PAGI app coderef.
Default
If no app is specified, serves the current directory:
pagi-server
# equivalent to: pagi-server PAGI::App::Directory root=.
EXAMPLES
# Serve current directory on default port
pagi-server
# Serve /var/www on port 8080
pagi-server -p 8080 PAGI::App::Directory root=/var/www
# Run an app file with HTTPS
pagi-server -a myapp.pl -p 3000 --ssl-cert cert.pem --ssl-key key.pem
# Run with multiple workers
pagi-server -w 4 ./myapp.pl
# Headless server / remote access (bind to all interfaces)
pagi-server --host 0.0.0.0 -p 8080 ./myapp.pl
# Production: all interfaces, workers, daemonized
pagi-server --host 0.0.0.0 -p 80 -w 4 -D --pid /var/run/pagi.pid ./myapp.pl
# Add library paths (like perl -I)
pagi-server -I ./lib -I ./local/lib ./myapp.pl
# Use EV event loop for better performance
pagi-server --loop EV ./myapp.pl
# Maximum performance (no access logging)
pagi-server --no-access-log -w 8 ./myapp.pl
# Optimal macOS performance (EV + kqueue)
LIBEV_FLAGS=8 pagi-server --loop EV --no-access-log ./myapp.pl
# High concurrency with SO_REUSEPORT (reduces accept contention)
pagi-server --reuseport -w 8 ./myapp.pl
# Workers restart after 10k requests (prevents memory growth)
pagi-server -w 4 --max-requests 10000 ./myapp.pl
# Limit max connections (prevents FD exhaustion)
pagi-server --max-connections 200 ./myapp.pl
# Enable debug logging
pagi-server --log-level debug ./myapp.pl
# Or via environment variable
IO_ASYNC_LOOP=EV pagi-server ./myapp.pl
# Run a proxy app
pagi-server PAGI::App::Proxy target=http://backend:3000
# Directory listing with hidden files
pagi-server PAGI::App::Directory root=/home show_hidden=1
BUNDLED APPS
The following PAGI::App::* modules are available:
- PAGI::App::Directory
-
Serve static files with directory listings.
pagi-server PAGI::App::Directory root=/var/www - PAGI::App::File
-
Serve static files (no directory listings).
pagi-server PAGI::App::File root=/var/www - PAGI::App::Proxy
-
HTTP reverse proxy.
pagi-server PAGI::App::Proxy target=http://backend:3000 - PAGI::App::Redirect
-
URL redirector.
pagi-server PAGI::App::Redirect location=https://example.com - PAGI::App::URLMap
-
Mount multiple apps at different paths.
- PAGI::App::Cascade
-
Try multiple apps in sequence.
See individual module documentation for available options.
ENVIRONMENT
- IO_ASYNC_LOOP
-
Specifies which IO::Async::Loop backend to use. The default is auto-detection. For better performance, especially with many concurrent connections:
IO_ASYNC_LOOP=EV pagi-server ./myapp.pl # Cross-platform (libev) IO_ASYNC_LOOP=Epoll pagi-server ./myapp.pl # Linux-specificThe
--loopoption takes precedence over this environment variable. - LIBEV_FLAGS
-
When using the EV backend, this controls which libev backend is used internally. By default, EV may choose a suboptimal backend (select instead of kqueue on macOS).
# Force kqueue on macOS for optimal performance (~6% faster) LIBEV_FLAGS=8 pagi-server --loop EV ./myapp.plValues: 1=select, 2=poll, 4=epoll (Linux), 8=kqueue (macOS/BSD)
EVENT LOOP BACKENDS
- Poll
-
Default fallback. Uses poll() system call. Works everywhere but slower with many connections.
- EV
-
High-performance backend using libev. Recommended for production. Install:
cpanm IO::Async::Loop::EV EV - Epoll
-
Linux-specific backend using epoll. O(1) complexity. Install:
cpanm IO::Async::Loop::Epoll - UV
-
Backend using libuv (Node.js's event library). Cross-platform. Install:
cpanm IO::Async::Loop::UV UV
SEE ALSO
PAGI::Runner, PAGI::Server, PAGI::App::Directory
AUTHOR
John Napiorkowski <jjnapiork@cpan.org>
LICENSE
This software is licensed under the same terms as Perl itself.