Deployment

A Punk application is a PSGI coderef; anything that serves PSGI serves it. It is fastest on Hyperman, the event-loop server it was built against.

Hyperman

hyperman --workers 4 --port 8080 app.psgi

Hyperman gives Punk three things other servers cannot:

  • Non-blocking futures - a pending Punk::Future parks on the worker's loop and the worker serves other requests meanwhile.
  • Detached WebSockets - detach hands the socket to the application, so connections do not pin workers.
  • The C ABI - Punk speaks to Hyperman through hm_abi.h directly, no PSGI-layer overhead on the hot paths.

HTTPS, and :80 beside :443

Hyperman binds several listeners in one run, each independently plain or TLS, so an ordinary site needs nothing in front of it:

Hyperman->run(
    app     => $app,
    listen  => [
        { port => 80,  redirect_https => 443 },
        { port => 443, tls_cert => $cert, tls_key => $key },
    ],
);

redirect_https answers every request on that listener with a 301 to the same host and path on the https port, in C, before the app is reached - so the plain port costs a parse and a write, not a dispatch.

This site ships that as a script. bin/punk-site makes a self-signed certificate on first run and serves both ports:

sudo bin/punk-site                  # 80 -> 301 -> 443
bin/punk-site --dev                 # 8080 / 8443, no root
bin/punk-site --cert /etc/letsencrypt/live/you/fullchain.pem \
              --key  /etc/letsencrypt/live/you/privkey.pem

Two things worth knowing before you point it at the internet:

  • Ports below 1024 need root, and Hyperman has no privilege-drop option, so a root start stays root. Prefer setcap cap_net_bind_service=+ep on the perl binary (Linux), a pf redirect to 8080/8443 (macOS), or a reverse proxy.
  • detach refuses a TLS socket, so an app with websocket or sse routes cannot serve them from a TLS listener. Terminate TLS in front of it instead - nginx, or the small terminator in Punk's example/Chat/bin/tls-proxy - and give Punk plain HTTP/1. That is why the Chat example is arranged the way it is, and why this site (pages, docs and JSON) is not.

Any PSGI server

plackup app.psgi

Everything works; the async paths block politely, and WebSocket routes need blocking => 1 (over psgix.io) or the app refuses to boot with routes it cannot serve.

Environments

PUNK_ENV=production hyperman app.psgi

selects the punk.production.yml config layer - see Config and secrets. Check it resolves before switching traffic:

PUNK_ENV=production punk config check

Frozen at boot

to_app compiles the whole application - routes, guards, controllers, templates registered, spec validated, markdown rendered, search indexed. Two consequences worth planning around:

  • Failures are boot failures. A typo'd controller, a missing template engine or an unresolvable secret stops the deploy, not the 3am request.
  • Content changes need a restart. A markdown mount serves frozen bytes; edit the docs, restart the app (in development, reload => 1 or punk dev re-renders on change).

Development

punk dev

serves on Hyperman with restart-on-change. punk doctor reports the environment - Perl, module versions, which C ABIs are live - when a deployment target misbehaves.

A checklist

  1. punk routes - the table you think you are shipping
  2. PUNK_ENV=production punk config check - secrets resolve
  3. punk doctor - ABIs live, versions expected
  4. prove -l t - the app boots and answers
  5. hyperman --workers N app.psgi