A lightning-fast Go application server that runs BareMetalPHP via a persistent pool of PHP workers β similar to FrankenPHP and RoadRunner, but lightweight, transparent, and tailored for your framework.
- β‘ Persistent PHP workers β no cold boots
- π§΅ Fast + slow worker pools with request classification
- π Static file serving (Go handles assets before PHP)
- π₯ Hot Reload (automatic worker restart on PHP file changes)
- π§© Config file support (
go_appserver.json) - π Automatic project root detection
- π¦ Graceful worker recycling (timeouts + max request count)
- π Binary protocol between Go and PHP workers
go mod init my-appgo get github.com/google/uuid
go get github.com/fsnotify/fsnotifyExpected directory layout:
my-app/
βββ go.mod
βββ cmd/
β βββ server/
β βββ main.go
β βββ config.go
βββ server/
β βββ worker.go
β βββ pool.go
β βββ server.go
βββ php/
β βββ worker.php
β βββ bridge.php
β βββ bootstrap_app.php
βββ routes/
β βββ web.php
βββ public/
go_appserver.json
{
"fast_workers": 4,
"slow_workers": 2,
"hot_reload": true,
"request_timeout_ms": 10000,
"max_requests_per_worker": 1000,
"static": [
{ "prefix": "/assets/", "dir": "public/assets" },
{ "prefix": "/css/", "dir": "public/css" },
{ "prefix": "/js/", "dir": "public/js" }
]
}If the file is missing, defaults are automatically applied.
From project root:
go run ./cmd/serverImportant:
Do not rungo run cmd/server/main.goβ Go will ignoreconfig.goand break the build.
Instead run the whole package:
go run ./cmd/server
Server will start on:
http://localhost:8080
ββββββββββββββββ
β Go HTTP Host β
ββββββββ¬ββββββββ
β
βββΊ tryServeStatic() β serves /assets/, /css/, /js/ directly
β
βββΊ BuildPayload() β JSON message β WorkerPool
β
βΌ
ββββββββββββββββββββββ
β PHP Worker (hot) β β Boots BareMetalPHP ONCE
β handle_bridge_req β
ββββββββββββββββββββββ
β
βΌ
JSON response β Go β Browser
Go = router + static host + supervisor
PHP = long-running application kernel
Enable via config:
{ "hot_reload": true }or environment variable:
export GO_PHP_HOT_RELOAD=1Hot reload watches for changes in:
php/routes/
When a file changes β workers marked dead β automatically restarted on next request.
my-app/
βββ cmd/server
β βββ main.go
β βββ config.go
βββ server
β βββ worker.go
β βββ pool.go
β βββ server.go
βββ php
β βββ bootstrap_app.php
β βββ bridge.php
β βββ worker.php
βββ routes
β βββ web.php
βββ public
β βββ index.html
βββ go_appserver.json
You're running:
go run cmd/server/main.go
Correct:
go run ./cmd/server
Go ignores files in the package if you run a single file.
This means:
- Worker crashed
- Worker hit max request limit
- Hot reload replaced workers
The pool recovers automatically.
Check:
- Your
prefixroutes public/existsgo_appserver.jsonhas correct directory names- No directory traversal (
../) errors
{
"fast_workers": 12,
"slow_workers": 4,
"hot_reload": false,
"request_timeout_ms": 30000,
"max_requests_per_worker": 5000,
"static": [
{ "prefix": "/", "dir": "public" },
{ "prefix": "/assets/", "dir": "public/assets" }
]
}- WebSocket support
- HTTP/2 + QUIC
- Native TLS termination
- Worker dashboard + metrics endpoint
- Fiber-style async adapters
- Zero-downtime worker rotation
MIT License.
You can do whatever you want with this code :)