Migration§
This guide covers two audiences:
- Users running archived Unit or a community-maintained package who want to switch to FreeUnit, the community-maintained LTS fork.
- Package maintainers of community repositories (Alpine, Arch, Gentoo,
*BSD, Nix, Remi, etc.) who need to rebase their recipes onto the
freeunitorg/freeunitsource tree.
Note
FreeUnit is ABI-compatible with previous Unit builds.
Functionally nothing changes — only the package name
(unit/nginx-unit → freeunit) and the upstream source URL
(github.com/nginx/unit → github.com/freeunitorg/freeunit).
config.json, runtime paths, CLI flags, and the control API stay as-is.
Warning
Official RPM/DEB packages for FreeUnit are coming soon. As of April 2026, FreeUnit is distributed via:
- Docker official images
- Source builds
- Pre-built
unitctlbinaries from GitHub Releases
Native package manager support (APT/YUM) is in active development. Track progress at: https://github.com/freeunitorg/freeunit/milestone/2
For users — pick one install path:
- Run our Docker official images, prepackaged with varied language combinations.
- To fine-tune FreeUnit to your goals, download the sources, install the toolchain, and build a custom binary from scratch.
- For RHEL/Fedora users who want to keep Remi’s PHP packages, try the hybrid approach (FreeUnit core + Remi PHP modules).
For package maintainers — rebase your recipe:
- Follow
Community Repository Maintainers
below for per-distro steps (source URL, rename,
provides/obsoletesaliases, checksums).
Note
The commands in this document starting with a hash (#) must be run as root or with superuser privileges.
Community Repository Maintainers§
This section targets downstream packagers who maintain a Unit recipe in a community repository (AUR, Alpine aports, Gentoo ::gentoo, FreeBSD ports, Remi’s RPM, etc.) and want to track FreeUnit going forward.
The rebase is mechanical:
- Change the source URL from
https://github.com/nginx/unittohttps://github.com/freeunitorg/freeunit. - Rename the package from
unit(ornginx-unit) tofreeunit. Rename subpackages accordingly (unit-php→freeunit-php,unit-python3→freeunit-python3, …). - Keep the old name as a
provides/replaces/obsoletesalias so existing installs upgrade cleanly. - Update signing/checksum metadata against the new release tarballs.
- Verify runtime paths still match the table at the end of each tab — upstream does not enforce a layout, so distro defaults are preserved.
Note
FreeUnit release tags follow the same 1.X.Y scheme as archived Unit,
starting from the last Unit release. Tarballs:
https://github.com/freeunitorg/freeunit/archive/refs/tags/<TAG>.tar.gz.
Warning
These distributions are maintained by their respective communities, not the FreeUnit project. Packages may be outdated or lack security updates.
Recipe: community/unit/APKBUILD in
alpinelinux/aports.
Rename the aport
Move
community/unit/tocommunity/freeunit/and updateAPKBUILD:community/freeunit/APKBUILD§pkgname=freeunit pkgver=1.35.3 source="$pkgname-$pkgver.tar.gz::https://github.com/freeunitorg/freeunit/archive/refs/tags/$pkgver.tar.gz" builddir="$srcdir/freeunit-$pkgver" subpackages="$pkgname-openrc $pkgname-doc $pkgname-dev $pkgname-perl $pkgname-php83 $pkgname-python3 $pkgname-ruby" provides="unit=$pkgver-r$pkgrel" replaces="unit"
Refresh checksums
$ abuild checksum
Build and test
$ abuild -r
Submit an MR to aports following the Alpine contributing guide.
Runtime details (unchanged from the legacy unit aport):
| Control socket | /run/control.unit.sock |
| Log file | /var/log/unit.log |
| Non-privileged user and group | unit |
Recipe: unit.spec in the Sisyphus
gears/u/unit repository.
Clone the gear and branch it
$ git clone git://git.altlinux.org/gears/u/unit.git freeunit $ cd freeunit
Update the spec
freeunit.spec§Name: freeunit Version: 1.35.3 Source0: https://github.com/freeunitorg/freeunit/archive/refs/tags/%version.tar.gz Provides: unit = %EVR Obsoletes: unit < %EVR
Rename subpackages the same way (
unit-perl→freeunit-perl,unit-php→freeunit-php, …).Rebuild in the hasher
$ gear-rpm -bb $ hsh --init && hsh --build freeunit-*.src.rpm
Push to Sisyphus via
gear-commit+git pushonce a maintainer has reviewed.
Runtime details (unchanged from the legacy unit gear):
| Control socket | /run/unit/control.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | _unit (mind the _ prefix) |
Recipe: PKGBUILD in the
nginx-unit AUR package.
Recommended path: publish a new AUR package named freeunit and
keep nginx-unit around for a release or two pointing at the same
tarball (provides=('nginx-unit'), conflicts=('nginx-unit')).
Clone the AUR repo and rename
$ git clone ssh://aur@aur.archlinux.org/freeunit.git $ cd freeunit
Update PKGBUILD
PKGBUILD§pkgbase=freeunit pkgname=('freeunit' 'freeunit-php' 'freeunit-python' 'freeunit-nodejs') pkgver=1.35.3 url='https://github.com/freeunitorg/freeunit' source=("freeunit-$pkgver.tar.gz::https://github.com/freeunitorg/freeunit/archive/refs/tags/$pkgver.tar.gz") provides=("nginx-unit=$pkgver") replaces=('nginx-unit') conflicts=('nginx-unit')
Refresh checksums and .SRCINFO
$ updpkgsums $ makepkg --printsrcinfo > .SRCINFO
Build in a clean chroot, then push
$ makepkg -s $ git commit -am "freeunit 1.35.3: rebase on freeunitorg/freeunit" $ git push
Warning
AUR packages are user-produced without pre-moderation.
Audit the PKGBUILD and any .install scripts before publishing.
Runtime details (unchanged from the legacy nginx-unit AUR package):
| Control socket | /run/unit/control.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | nobody |
Recipe: the www/unit port in
freebsd/freebsd-ports.
Strategy: create a new slave-free port www/freeunit and mark the
old www/unit port deprecated with MOVED redirect.
Copy the port tree
$ cp -R www/unit www/freeunit $ cp -R www/unit-php www/freeunit-php $ cp -R www/unit-python www/freeunit-python # …repeat for perl, ruby, wasm, libunit → libfreeunit
Update each Makefile
www/freeunit/Makefile§PORTNAME= freeunit DISTVERSION= 1.35.3 CATEGORIES= www MAINTAINER= you@example.org COMMENT= Dynamic application server, community LTS fork of NGINX Unit USE_GITHUB= yes GH_ACCOUNT= freeunitorg GH_PROJECT= freeunit CONFLICTS_INSTALL= unit-[0-9]* unit-php-[0-9]* unit-python-[0-9]* unit-perl-[0-9]* unit-ruby-[0-9]* unit-wasm-[0-9]*
Regenerate distinfo
$ make makesum
Register the rename in ``MOVED``
www/unit|www/freeunit|2026-04-19|Project renamed to FreeUnit
Test-build via poudriere, then submit a PR to freebsd-ports.
Runtime details (unchanged from the legacy www/unit port):
| Control socket | /var/run/unit/control.unit.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | www |
Recipe: www-servers/nginx-unit in the
::gentoo tree.
Strategy: add www-servers/freeunit and last-rite nginx-unit by
adding a dated block to profiles/package.mask (with a 30-day removal
notice) once the replacement is stable.
Create the new package directory
$ git mv www-servers/nginx-unit www-servers/freeunit $ cd www-servers/freeunit $ git mv nginx-unit-1.34.2.ebuild freeunit-1.35.3.ebuild
Update the ebuild
www-servers/freeunit/freeunit-1.35.3.ebuild§EAPI=8 DESCRIPTION="Dynamic application server, community LTS fork of NGINX Unit" HOMEPAGE="https://freeunit.org/" SRC_URI="https://github.com/freeunitorg/freeunit/archive/refs/tags/${PV}.tar.gz -> ${P}.tar.gz" S="${WORKDIR}/freeunit-${PV}" RDEPEND="!!<www-servers/nginx-unit-1.35" # hard block, forces removal before merge
Regenerate Manifest
$ pkgdev manifest
Test
$ sudo ebuild freeunit-1.35.3.ebuild clean merge $ pkgcheck scan
Submit a PR to gentoo/gentoo per the Gentoo developer manual.
Runtime details (unchanged from the legacy nginx-unit ebuild):
| Control socket | /run/unit.sock |
| Log file | /var/log/unit.log |
| Non-privileged user and group | nobody |
Recipe: www/unit in
pkgsrc.
Copy the category directory
$ cd pkgsrc/www $ cp -R unit freeunit
Update the Makefile
www/freeunit/Makefile§DISTNAME= freeunit-1.35.3 CATEGORIES= www MASTER_SITES= ${MASTER_SITE_GITHUB:=freeunitorg/freeunit/} GITHUB_TAG= ${PKGVERSION_NOREV} HOMEPAGE= https://freeunit.org/ COMMENT= Community LTS fork of NGINX Unit CONFLICTS+= unit-[0-9]* SUPERSEDES+= unit-[0-9]*
Do the same for
www/freeunit-perl,www/freeunit-python*,www/freeunit-ruby*,devel/libfreeunit.Regenerate distinfo
$ make distinfo
Test-build
$ cd www/freeunit && make package
Commit and send-pr via pkgsrc-wip first if the rename needs broader review.
Runtime details (unchanged from the legacy www/unit pkgsrc entry):
| Control socket | /var/run/unit/control.unit.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | unit |
Recipe: pkgs/servers/http/unit/default.nix in
NixOS/nixpkgs.
The NixOS module also lives at
nixos/modules/services/web-servers/unit/default.nix —
keep option names as aliases so existing configurations don’t break.
Rename the derivation directory
$ git mv pkgs/servers/http/unit pkgs/servers/http/freeunit
Update default.nix
pkgs/servers/http/freeunit/default.nix§stdenv.mkDerivation (finalAttrs: { pname = "freeunit"; version = "1.35.3"; src = fetchFromGitHub { owner = "freeunitorg"; repo = "freeunit"; rev = finalAttrs.version; hash = "sha256-..."; }; meta = with lib; { description = "Community LTS fork of NGINX Unit"; homepage = "https://freeunit.org/"; license = licenses.asl20; }; })
Add an alias in ``pkgs/top-level/aliases.nix``
unit = freeunit; # renamed 2026-04, remove after 25.11
Update the NixOS module to reference
pkgs.freeunitand add amkRenamedOptionModuleentry forservices.unit.*→services.freeunit.*.Refresh the hash and build
$ nix-prefetch-github --rev 1.35.3 freeunitorg freeunit # writes SRI hash for fetchFromGitHub (unpacked) $ nix-build -A freeunit $ nixos-rebuild build-vm -I nixpkgs=.
Submit a PR to nixpkgs.
Runtime details (unchanged from the legacy unit derivation):
| Control socket | /run/unit/control.unit.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | unit |
Recipe: www/unit in the
OpenBSD ports tree.
Create the new port
$ cd /usr/ports/www $ cp -R unit freeunit
Update the Makefile
www/freeunit/Makefile§COMMENT = community LTS fork of NGINX Unit GH_ACCOUNT = freeunitorg GH_PROJECT = freeunit GH_TAGNAME = 1.35.3 DISTNAME = freeunit-${GH_TAGNAME} PKGNAME = freeunit-${GH_TAGNAME} PSEUDO_FLAVORS = no_nonfree FLAVORS = php python ruby perl
Subpackage
PKGNAME``s follow: ``freeunit-php,freeunit-python,freeunit-perl,freeunit-ruby.Regenerate distinfo
$ cd /usr/ports/www/freeunit && make makesum
Record the rename for ``pkg_add -u``
OpenBSD has no ports-wide
MOVEDfile. Instead, add the old pkgstem as an@pkgpathline in the new port’spkg/PLISTso pkg_add -u auto-upgradesunit-*installs:www/freeunit/pkg/PLIST§@pkgpath www/unit @pkgpath www/unit,-main
Test-build and submit the diff to ports@openbsd.org per the OpenBSD porter’s handbook.
Runtime details (unchanged from the legacy www/unit port):
| Control socket | /var/run/unit/control.unit.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | _unit |
Recipe: Remi Collet builds from his own infrastructure — see blog.remirepo.net for the canonical spec tree and contact path. Coordinate the rename with Remi directly rather than opening a PR to the GitHub mirror.
The core package gets renamed; the PHP modules (phpXX-unit-php)
stay as-is — they are ABI-compatible with FreeUnit and Remi’s naming
scheme is valuable to existing users.
Fork the spec to ``freeunit.spec``
freeunit.spec§%global gh_owner freeunitorg %global gh_project freeunit Name: freeunit Version: 1.35.3 Release: 1%{?dist} Summary: Community LTS fork of NGINX Unit URL: https://freeunit.org/ Source0: https://github.com/%{gh_owner}/%{gh_project}/archive/refs/tags/%{version}.tar.gz Provides: unit = %{version}-%{release} Obsoletes: unit < %{version}-%{release} %prep %autosetup -n %{gh_project}-%{version}
Leave
--prefix=/usrin%configureso paths stay at/usr/sbin/unitdand/usr/lib64/unit/modules/for drop-in compatibility withphpXX-unit-php.Update ``phpXX-unit-php`` sub-specs
No rename required — only bump their
BuildRequires: freeunit-devel >= %{version}/Requires: freeunit >= %{version}lines so they link against the renamed core.Build and hand the SRPM to Remi
$ rpmbuild -ba freeunit.spec
The upload path into
rpms.remirepo.netis Remi-internal — don’t invent one. Send the resulting SRPM to Remi via the contact on blog.remirepo.net for repo ingestion.Verify the upgrade path
# dnf upgrade freeunit # /usr/sbin/unitd --version freeunit/1.35.3
Runtime details (unchanged from the legacy unit RPM):
| Control socket | /run/unit/control.sock |
| Log file | /var/log/unit/unit.log |
| Non-privileged user and group | nobody |
Docker Migration§
FreeUnit provides official Docker images for easy deployment.
Steps§
- Pick a tag and reference it
Images are hosted on GitHub Container Registry (GHCR): https://github.com/freeunitorg/freeunit/pkgs/container/freeunit
Tag format:
VERSION-VARIANT(pinned) orlatest-VARIANT(rolling). There is no barelatesttag.
Variant Description minimalNo language modules. Base for custom images. wasmWebAssembly Components (WASI 0.2) via Wasmtime. go1.24go1.25go1.26Go (single-version images). jsc17jsc21Java (Eclipse Temurin OpenJDK LTS). Runs .war/.jspapps.node20node22node24Node.js (single-version images). perl5.38perl5.40Perl (single-version images). php8.3php8.4php8.5PHP (single-version images). python3.12python3.12-slimPython 3.12, full and slim. python3.13python3.13-slimPython 3.13, full and slim. python3.14python3.14-slimPython 3.14, full and slim. ruby3.3ruby3.4Ruby (single-version images). Each tag is available for both
amd64andarm64architectures via a multi-arch manifest.Note
Language version support policy: FreeUnit supports each language variant for 1 year after the upstream language EOL. When a variant is retired, it is removed from the matrix and noted in
CHANGES.txt. EOL dates: https://endoflife.dateReference the image in your
Dockerfileordocker-compose.yml:Example — pinning PHP 8.4§FROM ghcr.io/freeunitorg/freeunit:1.35.3-php8.4Example — rolling latest Python 3.13§FROM ghcr.io/freeunitorg/freeunit:latest-python3.13
- Verify the image
$ docker run --rm ghcr.io/freeunitorg/freeunit:latest-minimal unitd --version freeunit/X.Y.Z # Example format; actual version may differNote
The
freeunit/prefix confirms the community fork.
- Deploy
Pull the image and start your containers:
# docker-compose pull # docker-compose up -d
Custom Images§
If you build custom images, use FreeUnit as your base:
FROM ghcr.io/freeunitorg/freeunit:minimal AS unit-base
# Your custom layers below
COPY --from=unit-base /usr/lib/unit/modules /usr/lib/unit/modules
# ... rest of your Dockerfile
Note
All runtime paths, environment variables, and entrypoint behavior remain unchanged. Your existing Docker configuration works as-is.
Build FreeUnit from Source§
For bare-metal deployments or when you need maximum control, build FreeUnit from source.
Prerequisites§
FreeUnit compiles and runs on various Unix-like operating systems, including:
- FreeBSD || 10 or later
- Linux || 2.6 or later
- macOS || 10.6 or later
- Solaris || 11
App languages and platforms that FreeUnit can run:
- Go || 1.24 or later
- Java || 17 or later (via JSC)
- Node.js || 20 or later
- PHP || 8.3, 8.4, 8.5
- Perl || 5.38 or later
- Python || 3.12, 3.13, 3.14
- Ruby || 3.3 or later
- WebAssembly Components WASI || 0.2
Optional dependencies:
- OpenSSL 1.0.1 or later for TLS support
- PCRE (8.0+) or PCRE2 (10.23+) for regex
- Wasmtime for WebAssembly support
Build and Install FreeUnit§
- Clone the FreeUnit repository
$ git clone https://github.com/freeunitorg/freeunit $ cd freeunit
- Configure the FreeUnit build
Enable the modules you need:
$ ./configure --openssl --otel \ --modules=php:8.4,python:3.13,nodejs:22,go1.25,ruby3.4,perl5.40Note
Run
./configure --helpto see all available options. For PHP, you can specify multiple versions:php:8.3,php:8.4,php:8.5. Supported language versions as of April 2026:
- PHP: 8.3, 8.4, 8.5
- Python: 3.12, 3.13, 3.14
- Node.js: 20, 22, 24
- Go: 1.24, 1.25, 1.26
- Ruby: 3.3, 3.4
- Perl: 5.38, 5.40
- Java (JSC): 17, 21 (OpenJDK LTS for .war/.jsp applications)
- WebAssembly: WASI 0.2
- Compile and install FreeUnit
$ make # make install # or: sudo make installThis installs: -
unitdto/usr/local/sbin/-unitctlto/usr/local/bin/- Modules to/usr/local/lib/unit/modules/- Default config directory:/usr/local/etc/unit/
- Set up systemd service (optional but recommended)
Copy the provided unit file:
# cp pkg/systemd/unit.service /etc/systemd/system/ # systemctl daemon-reload # systemctl enable --now unit
- Verify the FreeUnit installation
$ unitd --version freeunit/X.Y.Z
Runtime Compatibility§
FreeUnit preserves full compatibility with previous Unit builds:
| Control socket | /run/unit/control.sock (unchanged) |
| Log file | /var/log/unit/unit.log (unchanged) |
| Configuration path | /etc/unit/config.json (unchanged) |
| State directory | /var/lib/unit/ (unchanged) |
| User/group defaults | unit:unit or distribution default (unchanged) |
| Systemd service name | unit.service (unchanged) |
Troubleshooting FreeUnit§
FreeUnit fails to start after migration
If FreeUnit doesn’t start after updating:
Check the log:
# journalctl -u unit -n 50 # tail -f /var/log/unit/unit.log
Verify module compatibility:
# ls -la /usr/lib64/unit/modules/ # RHEL/Fedora # ls -la /usr/lib/unit/modules/ # Debian/Ubuntu
Ensure matching versions:
$ unitd --version $ rpm -qa | grep unit # or: dpkg -l | grep unit
All
unit-*components should share the same version.Restart with debug output:
# systemctl stop unit # unitd --no-daemon --control /run/unit/control.sock --log /var/log/unit/unit.log
PHP module not detected in FreeUnit
If your PHP application isn’t recognized by FreeUnit:
Confirm the module is installed or built:
# rpm -qa | grep unit-php # RHEL/Fedora (Remi packages) # ./configure --help | grep php # Source build check
Check module loading:
# unitd --test-config /etc/unit/config.json
Restart FreeUnit to reload modules:
# systemctl restart unit
Verify PHP SAPI in your app config:
Example application config§{ "type": "php", "root": "/var/www/myapp", "index": "index.php", "script": "index.php" }
Rollback Procedure§
If you need to revert to the previous setup:
- Stop FreeUnit
# systemctl stop unit
- Restore previous binary or image
Revert the image tag in your
docker-compose.ymlorDockerfile:FROM ghcr.io/freeunitorg/freeunit:1.35.0-php8.4 # Previous pinned version; replace variant to match your imageThen redeploy:
# docker-compose pull # docker-compose up -dRebuild the previous version from source:
$ git clone https://github.com/freeunitorg/freeunit $ cd freeunit $ git checkout 1.35.0 # Or your previous version tag $ ./configure --openssl $ make # make installReinstall the original package from your distribution:
# apk add unit$ yay -S unit# pkg install unit# emerge www-servers/unit# dnf downgrade unit # or: dnf install unit-1.35.0-1.remi
- Restart
# systemctl start unit
Note
Configuration files in /etc/unit/ and application data
in /var/lib/unit/ are preserved during binary changes.
FAQ§
Q: Will my config.json break after migrating to FreeUnit?
No. FreeUnit maintains 100% compatibility with previous Unit builds.
Your config.json works as-is.
Q: Can I mix FreeUnit core with Remi’s PHP modules?
Yes. FreeUnit’s core binary is ABI-compatible with Remi’s
phpXX-unit-php modules. This is the basis of the
hybrid approach.
Q: How do I verify I’m running FreeUnit?
Check the version string:
$ unitd --version
freeunit/X.Y.Z # Example format; actual version may differ
The freeunit/ prefix confirms the community fork.
Q: What if I need PHP 8.2 or older?
FreeUnit provides modules for PHP 8.3, 8.4, and 8.5. For older PHP versions, consider:
- Using Remi’s modules with FreeUnit core (compatible)
- Building legacy modules from source with custom
./configureflags - Containerizing legacy apps with Docker using pinned base images
Q: Is there a migration script for FreeUnit?
A helper script is planned for a future release. Track progress on our roadmap: https://github.com/freeunitorg/freeunit/milestone/3
Until then, follow the manual steps above — they take <5 minutes.
Q: Where is the GPG key for FreeUnit package verification?
Official RPM/DEB packages for FreeUnit are not yet available. When they are released, the public key will be published at: http://freeunit.org/media/keys/6C68B7AA.asc
For source builds, verify commits via GitHub signatures: https://github.com/freeunitorg/freeunit/commits/main
See also
- Installation — Install FreeUnit from scratch
- configuration — Application configuration reference
- Community — Get help from the FreeUnit community