reversebin

package module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 24 Imported by: 0

README

caddy-reverse-bin

caddy-reverse-bin is a modern CGI-bin for reverse proxies: drop in an executable, let Caddy spawn it on demand, and proxy requests to it.

This repository contains the Caddy plugin, its development Caddy binary entrypoint, and plugin tests.

A Debian/systemd hosting setup derived from this lives in https://github.com/tarasglek/reverse-bin-hosting.

Caddyfile usage

:8080 {
	reverse-bin {
		exec ./app
		dir /path/to/app
		reverse_proxy_to 127.0.0.1:9000
		health_check GET /health
	}
}

Common subdirectives:

  • exec <command> [args...]: command to launch on demand.
  • dir <path>: working directory for the command.
  • env KEY=value...: environment variables for the command.
  • pass_env KEY...: pass selected parent environment variables.
  • pass_all_env: pass the full parent environment.
  • reverse_proxy_to <upstream>: static upstream address, such as 127.0.0.1:9000 or unix//tmp/app.sock.
  • health_check <METHOD> <PATH> [STATUS]: health probe before proxying. Without STATUS, any 2xx or 3xx response is accepted.
  • idle_timeout_ms <ms>: stop the child process after it has been idle for this long.
  • health_timeout_ms <ms>: timeout for health checks.
  • termination_grace_ms <ms>: graceful termination timeout.
  • termination_kill_wait_ms <ms>: delay before force-killing a process after graceful termination fails.
  • dynamic_proxy_detector <command> [args...]: command that discovers launch/proxy settings dynamically; see the sample detector docs.

Unix socket upstreams use reverse_proxy_to unix//path/to/app.sock. For Unix sockets, reverse-bin treats the socket file becoming available as readiness, so health_check is optional. TCP/HTTP static upstreams require health_check so the handler can tell when the launched process is ready.

Health checks

Health checks are used to ensure the launched app has finished starting before Caddy proxies traffic to it. By default, health_check accepts any 2xx or 3xx response. Use an explicit status for auth-protected routes, for example health_check GET /v2/ 401. Apps that require auth should expose a public /health endpoint or configure the expected redirect/status.

For reverse-bin-hosting, configure the probe with environment variables:

REVERSE_BIN_HEALTH_METHOD=GET
REVERSE_BIN_HEALTH_PATH=/
REVERSE_BIN_HEALTH_STATUS=302

Motivation

In the 2000s one could set up multi-user web servers with the Apache UserDir module, enable cgi-bin with Perl, or enable mod_php. There was no CI/CD; one would often just edit in production. There were plenty of security and performance problems with this, but the edit/deploy cycle was incredible and collaboration was immediate. You could just mkdir or copy an existing site and edit files with immediate results.

This Caddy reverse-bin module is my attempt to combine that old-school dev UX with Unix-style process composition and modern reverse-proxy/load-balancer approach. I decided to extend Caddy because it is sufficiently configurable to cover a wide varity of hosting needs and is written in a fairly safe language. The caddy-reverse-bin module enables the following:

  • On-demand servers that scale down when idle: e.g. spawn npm run dev (or some equivalent) when traffic hits your app, then kill it after some idle timeout
  • Dynamic detector to decouple app-shape detection from web-server core; see examples/reverse-proxy/ and its sample detector
  • A process-spawning model that is great for delegating security to something like landrun or VMs like smolvm
  • Hosting on a shared SSH server for collaboration
  • SSH also enables CI/CD via the magic of git config receive.denyCurrentBranch updateInstead

For common app-server auto-detection and the more opinionated hosting setup, see https://github.com/tarasglek/reverse-bin-hosting.

This project came out of feelings of nostalgia for the classic 2000s dev-loop brought on by trying https://www.smallweb.run/. Less is more: a simple process-based reverse proxy gets your surprisingly far; no need for hyperscale cargo culting.

Note I used LLMs heavily in this project, it still took over 4-months of spare-time to develop this concept into code I can use for person projects.

Development

Run the full local verification suite, including Go tests and the example HTTP smoke test:

make check

Build a local Caddy binary with this plugin:

make build
./caddy list-modules | grep http.handlers.reverse-bin

Documentation

Overview

Package reversebin provides a Caddy HTTP handler (`reverse-bin`) that starts an executable backend and proxies requests to it.

The module is intended for backends that should be started on demand and terminated after inactivity.

Index

Constants

This section is empty.

Variables

View Source
var (
	Version   = "dev"
	Commit    = "none"
	BuildDate = "unknown"
)

Build-time metadata set via -ldflags during release builds.

Functions

This section is empty.

Types

type DetectorOutput added in v0.2.2

type DetectorOutput = detectorschema.DetectorOutput

DetectorOutput is the JSON object a dynamic proxy detector writes to stdout.

type ReverseBin

type ReverseBin struct {
	// Name of executable script or binary and its arguments
	Executable []string `json:"executable"`
	// Working directory (default, current Caddy working directory)
	WorkingDirectory string `json:"workingDirectory,omitempty"`
	// Environment key value pairs (key=value) for this particular app
	Envs []string `json:"envs,omitempty"`
	// Environment keys to pass through for all apps
	PassEnvs []string `json:"passEnvs,omitempty"`
	// True to pass all environment variables to the executable
	PassAll bool `json:"passAllEnvs,omitempty"`

	// Address to proxy to (for proxy mode)
	ReverseProxyTo string `json:"reverse_proxy_to,omitempty"`
	// Health check method (GET or HEAD)
	HealthMethod string `json:"healthMethod,omitempty"`
	// Health check path
	HealthPath string `json:"healthPath,omitempty"`
	// Exact health check status; zero accepts any 2xx/3xx response
	HealthStatus int `json:"healthStatus,omitempty"`
	// Binary and arguments to run to determine proxy parameters dynamically
	DynamicProxyDetector []string `json:"dynamic_proxy_detector,omitempty"`
	// Idle timeout in milliseconds before stopping backend process after last request
	IdleTimeoutMS int `json:"idleTimeoutMs,omitempty"`
	// Health timeout in milliseconds before startup fails
	HealthTimeoutMS int `json:"healthTimeoutMs,omitempty"`
	// Termination grace in milliseconds before SIGKILL
	TerminationGraceMS int `json:"terminationGraceMs,omitempty"`
	// Kill wait in milliseconds after SIGKILL before reporting failure
	TerminationKillWaitMS int `json:"terminationKillWaitMs,omitempty"`
	// contains filtered or unexported fields
}

ReverseBin supervises executable backends and proxies HTTP traffic to them.

func (*ReverseBin) CaddyModule

func (c *ReverseBin) CaddyModule() caddy.ModuleInfo

func (*ReverseBin) Cleanup

func (c *ReverseBin) Cleanup() error

func (*ReverseBin) GetUpstreams

func (c *ReverseBin) GetUpstreams(r *http.Request) ([]*reverseproxy.Upstream, error)

GetUpstreams implements reverseproxy.UpstreamSource which allows dynamic selection of backend process ensures process is running before returning the upstream address to the proxy. Note: In Caddy's reverse_proxy, GetUpstreams is called before ServeHTTP. For the very first request that triggers a process start, the request tracking must be initialized here to ensure the idle timer starts correctly after the first request completes.

func (*ReverseBin) Provision

func (c *ReverseBin) Provision(ctx caddy.Context) error

Provision implements caddy.Provisioner; it sets up the module's internal state and provisions the underlying reverse proxy handler.

func (*ReverseBin) ServeHTTP

func (c *ReverseBin) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error

ServeHTTP implements caddyhttp.MiddlewareHandler; it handles the HTTP request manages idle process killing

func (*ReverseBin) UnmarshalCaddyfile

func (c *ReverseBin) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler; it parses the reverse-bin directive and its subdirectives from the Caddyfile.

Directories

Path Synopsis
cmd
caddy command
Package main is the entry point of the Caddy application.
Package main is the entry point of the Caddy application.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL