FreeUnit Docker images for PHP 7.1–7.4 – a migration bridge to PHP 8.x (Nginx Unit-based)
  • C 57.6%
  • Python 15.9%
  • Java 13.5%
  • Rust 5%
  • Shell 3.1%
  • Other 4.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Claude 0226330917 fix(http): parse the chunked trailer section instead of erroring (#106)
nxt_http_chunk_parse() did not implement the HTTP/1.1 trailer section
(RFC 9112 7.1.2): after the terminal 0-chunk, sw_chunk_end_newline
accepted only CR, so the first byte of a trailer field hit chunk_error.
In the proxy response relay (nxt_h1p_peer_body_process) that aborted an
already-streaming response, truncating the body at a racy point (#106);
chunked requests carrying a trailer were rejected the same way.

Consume the trailer section: after the terminal 0-chunk, skip zero or
more trailer field lines and terminate on the final CRLF. hcp->last is
deferred until the whole terminal sequence, including any trailer, has
been consumed, so a buffer ending mid-trailer relays the body so far and
resumes via the saved parser state instead of desyncing keepalive. The
trailer is consumed only for framing and never exposed -- per RFC 9112
7.1.2 a recipient MAY discard trailer fields. An unbounded trailer is
capped at 4096 consumed bytes.

Trailer field values are discarded unparsed, but the line grammar is
validated: every line must be a field line -- a tchar field-name
(RFC 9110 5.1, 5.6.2), then ':', then a value free of the CTL bytes
RFC 9110 5.5 forbids (%x00-1F / %x7F other than HTAB, which also rejects
a bare LF).

Skipping the lines unvalidated -- the nginx ngx_http_parse_chunked()
behaviour -- would make Unit swallow a line like "GET /admin HTTP/1.1",
printable and CTL-free, as a field to be discarded.  A peer that ends the
message at the terminal CRLF instead reads those bytes as the start of
the next request, so one side answers a request the other consumed: a
request smuggled past whichever side is more lenient.  master rejects
trailers outright, so this PR is what would introduce that surface, and
the grammar check is what keeps it closed.  An empty field-name, a space
before the colon (RFC 9112 5.1), non-token bytes in the name, and a
leading SP or HTAB -- the obs-fold continuation RFC 9112 5.2 forbids in a
trailer section -- are refused for the same reason.

test_proxy_chunked_invalid asserted a non-200 response for an upstream
that closes right after the terminal 0-chunk's trailer.  A trailer left
unterminated by the close is no longer a framing error: it relays as 200
with a truncated body (no terminal 0-chunk downstream), matching the
incomplete-data-chunk case next to it, so the reclassified assertion now
uses a well-formed line, '0\r\nX-T: v'.  Its previous input, '0\r\nX',
reaches the parser as the complete line "X\r\n" -- run_server terminates
every echoed line with CRLF -- which the field-line grammar rejects, so
that input stays non-200 (502) and is asserted explicitly rather than
through the "not 200" helper: accepting either outcome would let the
grammar check be removed without a test noticing.  The xfail marker on
test_proxy_chunked_trailer is removed.

Tests:
- src/test/nxt_http_chunk_parse_test.c: single/multi trailers, HTAB and
  obs-text in a value, empty value, incomplete terminals, the 4096 cap,
  split-buffer resumption, and a runtime sweep over every CTL byte
  (%x00-1F and %x7F, CR excepted as the line terminator) in both trailer
  positions -- HTAB accepted mid-value, every control byte rejected at a
  line start, where it is an obs-fold rather than a field-name.  Grammar
  rejections cover the colonless request line, a name with no colon, an
  empty name, a space before the colon, a non-tchar name byte and both
  obs-fold forms.  The oversized-trailer case now overflows the cap
  inside a well-formed value: a bare run of 'X' would be refused by the
  grammar first and would have stopped testing the cap.  The cap itself
  is NXT_HTTP_TRAILER_MAX_SIZE rather than a literal repeated at each of
  the four counting sites.
- test/test_chunked.py::test_chunked_trailer: request-side integration
  test for the chunked-request -> Content-Length path (single, multiple,
  colon-tight and empty-value trailers relay body intact; the five
  malformed field lines and an oversized trailer -> 400).
- test/test_chunked.py::test_chunked_split_reads gains two trailer-shaped
  boundaries, which only became reachable here: a first read ending inside
  a trailer field line, and a trailer line arriving in its own read.  Both
  leave the header buffer carrying framing bytes only, the shape that
  reaches the parser's buffer recycle.

Fixes https://github.com/freeunitorg/freeunit/issues/106

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhMcvYuSz1p4A3obfTt3xA
2026-07-27 18:24:06 +02:00
.github fix(libunit): do not replay a stale SCM_RIGHTS when port_recv returns nothing 2026-07-26 17:08:11 +02:00
auto fix(libunit): do not replay a stale SCM_RIGHTS when port_recv returns nothing 2026-07-26 17:08:11 +02:00
docs fix: post-merge review cleanups (proxy keepalive, cgroup dir leak) 2026-07-17 06:19:06 +02:00
fuzzing http: inline_fields[16] embedded header array for request and response 2026-07-24 13:52:03 +02:00
go fix(libunit): do not replay a stale SCM_RIGHTS when port_recv returns nothing 2026-07-26 17:08:11 +02:00
pkg fix(contrib): correct wasi-sysroot-27.0 SHA512SUM 2026-07-17 08:57:25 +02:00
scripts feat(scripts): add unfreeze-sync.sh and unfreeze-labels.sh 2026-05-01 10:24:09 +03:00
src fix(http): parse the chunked trailer section instead of erroring (#106) 2026-07-27 18:24:06 +02:00
test fix(http): parse the chunked trailer section instead of erroring (#106) 2026-07-27 18:24:06 +02:00
tools build(unitctl): refresh dependency crates for the 1.36.0 release 2026-07-17 02:34:18 +02:00
.editorconfig .editorconfig: fix bracket balance of editorconfig file 2024-11-12 09:50:02 -08:00
.gitattributes do not exclude unit-openapi.yaml from releases 2025-10-06 17:31:10 +01:00
.gitignore feat(otel)!: upgrade opentelemetry crates 0.24 → 0.32 2026-05-30 12:48:22 +03:00
.mailmap .mailmap: Add entry for Dave McAllister 2025-09-03 21:06:15 +01:00
.rustfmt.toml Add a .rustfmt.toml file 2024-02-21 16:20:32 +00:00
CHANGES fix: post-merge review cleanups (proxy keepalive, cgroup dir leak) 2026-07-17 06:19:06 +02:00
CODE_OF_CONDUCT.md feat(pkg): rebrand RPM packaging and add EOL/community docs 2026-04-28 02:26:01 +03:00
configure http: Wire up HTTP compression to the build system 2025-04-14 18:11:53 +01:00
CONTRIBUTING.md feat(otel): semconv attrs, app-aware spans, gRPC, 5xx errors 2026-05-31 18:17:31 +03:00
EOL.md data(eol): add java 25 (LTS) + java 26 runtimes with Dockerfiles 2026-07-15 16:23:20 +02:00
LICENSE Added LICENSE and NOTICE files. 2017-09-06 18:26:37 +03:00
NOTICE Update copyright notice 2025-01-14 04:17:34 +00:00
README.md ci(build-test): check ci cache 2026-06-15 01:47:58 +03:00
RELEASE-PROCESS.md chore(unitctl): bump crate versions to 1.36.0 2026-07-17 02:34:18 +02:00
SECURITY.md chore(security): supported-versions matrix for 1.36.0 2026-07-17 17:49:51 +02:00
TODO.md refactor(ci): restructure workflows, align Docker naming end-to-end, fix wasm builds 2026-06-15 00:24:40 +03:00
UNFREEZE.md feat(scripts): add unfreeze-sync.sh and unfreeze-labels.sh 2026-05-01 10:24:09 +03:00
version 1.36.0 2026-07-17 02:34:18 +02:00

FreeUnit — Community LTS fork of Unit

Project Status: Active – The project has reached a stable, usable state and is being actively developed. Build & Test Docker GitHub Discussions

Free as in freedom.

Unit application server, continued by the community.

The original Unit repository was archived in October 2025. The maintainers noted: "A new maintainer is desired." FreeUnit is that maintainer.

Forked from the original Unit project to ensure:

  • Long-term security maintenance
  • PHP 8.4+ and PHP 8.5+ runtime support
  • Predictable release cycle
  • Independent, community-driven governance

In the lineage of: freenginx · MariaDB · LibreOffice · OpenSSH — when corporations step back, community takes over.


Universal Web App Server

FreeUnit is a lightweight and versatile open-source server with two primary capabilities:

  • serves static media assets
  • runs application code in eight languages

Unit compresses several layers of the modern application stack into a potent, coherent solution with a focus on performance, low latency, and scalability. It is intended as a universal building block for any web architecture, regardless of its complexity, from enterprise-scale deployments to your pet's homepage.

Its native RESTful JSON API enables dynamic updates with zero interruptions and flexible configuration, while its out-of-the-box productivity reliably scales to production-grade workloads. We achieve that with a complex, asynchronous, multithreading architecture comprising multiple processes to ensure security and robustness while getting the most out of today's computing platforms.

Installation

Docker

Images are published to the GitHub Container Registry (GHCR) on every release and are available for linux/amd64 and linux/arm64.

Variant Image
minimal ghcr.io/freeunitorg/freeunit:latest-minimal
PHP 8.5 ghcr.io/freeunitorg/freeunit:latest-php-8.5
PHP 8.4 ghcr.io/freeunitorg/freeunit:latest-php-8.4
PHP 8.3 ghcr.io/freeunitorg/freeunit:latest-php-8.3
Python 3.14 ghcr.io/freeunitorg/freeunit:latest-python-3.14
Python 3.13 ghcr.io/freeunitorg/freeunit:latest-python-3.13
Python 3.12 ghcr.io/freeunitorg/freeunit:latest-python-3.12
Node.js 26 ghcr.io/freeunitorg/freeunit:latest-node-26
Node.js 24 ghcr.io/freeunitorg/freeunit:latest-node-24
Node.js 22 ghcr.io/freeunitorg/freeunit:latest-node-22
Node.js 20 ghcr.io/freeunitorg/freeunit:latest-node-20
Go 1.26 ghcr.io/freeunitorg/freeunit:latest-go-1.26
Go 1.25 ghcr.io/freeunitorg/freeunit:latest-go-1.25
Ruby 4.0 ghcr.io/freeunitorg/freeunit:latest-ruby-4.0
Ruby 3.4 ghcr.io/freeunitorg/freeunit:latest-ruby-3.4
WebAssembly ghcr.io/freeunitorg/freeunit:latest-wasm

Full list of variants (including python-3.14, perl-5.40, ruby-3.3, slim Python variants, etc.) is in the docker workflow.

$ docker pull ghcr.io/freeunitorg/freeunit:latest-minimal
$ mkdir /tmp/unit-control
$ docker run -d \
      --mount type=bind,src=/tmp/unit-control,dst=/var/run \
      --mount type=bind,src=.,dst=/www \
      --network host \
      ghcr.io/freeunitorg/freeunit:latest-minimal

Build from Source

$ git clone https://github.com/freeunitorg/freeunit
$ cd freeunit
$ ./configure --openssl --otel
$ make
$ sudo make unitd-install

Getting Started with unitctl

unitctl streamlines the management of FreeUnit processes through an easy-to-use command line interface. Download it from the releases page.

$ tar xzvf unitctl-master-x86_64-unknown-linux-gnu.tar.gz
# mv unitctl /usr/local/bin/

Quick Start: PHP

Save /www/helloworld/index.php:

<?php echo "Hello, PHP on FreeUnit!"; ?>

Configure via Unix socket:

# curl -X PUT --data-binary @config.json \
       --unix-socket /var/run/control.unit.sock \
       http://localhost/config/applications
# curl -X PUT -d '{"127.0.0.1:8080": {"pass": "applications/helloworld"}}' \
       --unix-socket /var/run/control.unit.sock \
       http://localhost/config/listeners
$ curl 127.0.0.1:8080
Hello, PHP on FreeUnit!

PHP 8.4 and PHP 8.5 Support

FreeUnit provides first-class PHP 8.4 and PHP 8.5 support — the primary motivation for this fork.

$ docker pull ghcr.io/freeunitorg/freeunit:latest-php-8.5
$ docker pull ghcr.io/freeunitorg/freeunit:latest-php-8.4

OpenTelemetry

FreeUnit includes built-in OpenTelemetry support (compiled with --otel):

{
  "settings": {
    "telemetry": {
      "endpoint": "http://localhost:4317/v1/traces",
      "protocol": "grpc",
      "sampling_ratio": 1.0,
      "batch_size": 20
    }
  }
}

WebAssembly

FreeUnit supports running WebAssembly Components (WASI 0.2). For configuration details see the OpenAPI spec.

OpenAPI Specification

The OpenAPI specification aims to simplify configuring and integrating FreeUnit deployments and provides an authoritative source of knowledge about the control API.

Community

License

FreeUnit is distributed under the Apache 2.0 License, same as the original Unit project.


Forked from nginx/unit — original authors retain full credit.