FreeUnit

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/freeunit source tree.

Note

FreeUnit is ABI-compatible with previous Unit builds. Functionally nothing changes — only the package name (unit/nginx-unitfreeunit) and the upstream source URL (github.com/nginx/unitgithub.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:

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:

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:

  1. Change the source URL from https://github.com/nginx/unit to https://github.com/freeunitorg/freeunit.
  2. Rename the package from unit (or nginx-unit) to freeunit. Rename subpackages accordingly (unit-phpfreeunit-php, unit-python3freeunit-python3, …).
  3. Keep the old name as a provides/replaces/obsoletes alias so existing installs upgrade cleanly.
  4. Update signing/checksum metadata against the new release tarballs.
  5. 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.

  1. Rename the aport

    Move community/unit/ to community/freeunit/ and update APKBUILD:

    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"
    
  2. Refresh checksums

    $ abuild checksum
    
  3. Build and test

    $ abuild -r
    
  4. 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.

  1. Clone the gear and branch it

    $ git clone git://git.altlinux.org/gears/u/unit.git freeunit
    $ cd freeunit
    
  2. 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-perlfreeunit-perl, unit-phpfreeunit-php, …).

  3. Rebuild in the hasher

    $ gear-rpm -bb
    $ hsh --init && hsh --build freeunit-*.src.rpm
    
  4. Push to Sisyphus via gear-commit + git push once 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')).

  1. Clone the AUR repo and rename

    $ git clone ssh://aur@aur.archlinux.org/freeunit.git
    $ cd freeunit
    
  2. 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')
    
  3. Refresh checksums and .SRCINFO

    $ updpkgsums
    $ makepkg --printsrcinfo > .SRCINFO
    
  4. 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.

  1. 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
    
  2. 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]*
    
  3. Regenerate distinfo

    $ make makesum
    
  4. Register the rename in ``MOVED``

    www/unit|www/freeunit|2026-04-19|Project renamed to FreeUnit
    
  5. 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.

  1. 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
    
  2. 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
    
  3. Regenerate Manifest

    $ pkgdev manifest
    
  4. Test

    $ sudo ebuild freeunit-1.35.3.ebuild clean merge
    $ pkgcheck scan
    
  5. 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.

  1. Copy the category directory

    $ cd pkgsrc/www
    $ cp -R unit freeunit
    
  2. 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.

  3. Regenerate distinfo

    $ make distinfo
    
  4. Test-build

    $ cd www/freeunit && make package
    
  5. 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.

  1. Rename the derivation directory

    $ git mv pkgs/servers/http/unit pkgs/servers/http/freeunit
    
  2. 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;
      };
    })
    
  3. Add an alias in ``pkgs/top-level/aliases.nix``

    unit = freeunit;  # renamed 2026-04, remove after 25.11
    
  4. Update the NixOS module to reference pkgs.freeunit and add a mkRenamedOptionModule entry for services.unit.*services.freeunit.*.

  5. 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=.
    
  6. 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.

  1. Create the new port

    $ cd /usr/ports/www
    $ cp -R unit freeunit
    
  2. 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.

  3. Regenerate distinfo

    $ cd /usr/ports/www/freeunit && make makesum
    
  4. Record the rename for ``pkg_add -u``

    OpenBSD has no ports-wide MOVED file. Instead, add the old pkgstem as an @pkgpath line in the new port’s pkg/PLIST so pkg_add -u auto-upgrades unit-* installs:

    www/freeunit/pkg/PLIST§
    @pkgpath www/unit
    @pkgpath www/unit,-main
    
  5. 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.

  1. 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=/usr in %configure so paths stay at /usr/sbin/unitd and /usr/lib64/unit/modules/ for drop-in compatibility with phpXX-unit-php.

  2. 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.

  3. Build and hand the SRPM to Remi

    $ rpmbuild -ba freeunit.spec
    

    The upload path into rpms.remirepo.net is Remi-internal — don’t invent one. Send the resulting SRPM to Remi via the contact on blog.remirepo.net for repo ingestion.

  4. 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§

  1. 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) or latest-VARIANT (rolling). There is no bare latest tag.

Variant Description
minimal No language modules. Base for custom images.
wasm WebAssembly Components (WASI 0.2) via Wasmtime.
go1.24 go1.25 go1.26 Go (single-version images).
jsc17 jsc21 Java (Eclipse Temurin OpenJDK LTS). Runs .war/.jsp apps.
node20 node22 node24 Node.js (single-version images).
perl5.38 perl5.40 Perl (single-version images).
php8.3 php8.4 php8.5 PHP (single-version images).
python3.12 python3.12-slim Python 3.12, full and slim.
python3.13 python3.13-slim Python 3.13, full and slim.
python3.14 python3.14-slim Python 3.14, full and slim.
ruby3.3 ruby3.4 Ruby (single-version images).

Each tag is available for both amd64 and arm64 architectures 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.date

Reference the image in your Dockerfile or docker-compose.yml:

Example — pinning PHP 8.4§
FROM ghcr.io/freeunitorg/freeunit:1.35.3-php8.4
Example — rolling latest Python 3.13§
FROM ghcr.io/freeunitorg/freeunit:latest-python3.13
  1. Verify the image
$ docker run --rm ghcr.io/freeunitorg/freeunit:latest-minimal unitd --version
freeunit/X.Y.Z   # Example format; actual version may differ

Note

The freeunit/ prefix confirms the community fork.

  1. 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:

Dockerfile§
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§

  1. Clone the FreeUnit repository
$ git clone https://github.com/freeunitorg/freeunit
$ cd freeunit
  1. 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.40

Note

Run ./configure --help to 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
  1. Compile and install FreeUnit
$ make
# make install   # or: sudo make install

This installs: - unitd to /usr/local/sbin/ - unitctl to /usr/local/bin/ - Modules to /usr/local/lib/unit/modules/ - Default config directory: /usr/local/etc/unit/

  1. 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
  1. 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:

  1. Check the log:

    # journalctl -u unit -n 50
    # tail -f /var/log/unit/unit.log
    
  2. Verify module compatibility:

    # ls -la /usr/lib64/unit/modules/  # RHEL/Fedora
    # ls -la /usr/lib/unit/modules/    # Debian/Ubuntu
    
  3. Ensure matching versions:

    $ unitd --version
    $ rpm -qa | grep unit  # or: dpkg -l | grep unit
    

    All unit-* components should share the same version.

  4. 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:

  1. Confirm the module is installed or built:

    # rpm -qa | grep unit-php  # RHEL/Fedora (Remi packages)
    # ./configure --help | grep php  # Source build check
    
  2. Check module loading:

    # unitd --test-config /etc/unit/config.json
    
  3. Restart FreeUnit to reload modules:

    # systemctl restart unit
    
  4. 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:

  1. Stop FreeUnit
# systemctl stop unit
  1. Restore previous binary or image

Revert the image tag in your docker-compose.yml or Dockerfile:

FROM ghcr.io/freeunitorg/freeunit:1.35.0-php8.4   # Previous pinned version; replace variant to match your image

Then redeploy:

# docker-compose pull
# docker-compose up -d

Rebuild 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 install

Reinstall 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
  1. 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 ./configure flags
  • 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