Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 15 additions & 19 deletions .github/workflows/static.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,11 @@ jobs:
matrix:
platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
debug: [false]
mimalloc: [false]
include:
- platform: linux/amd64
- platform: linux/amd64
debug: true
- platform: linux/amd64
mimalloc: true
name: Build ${{ matrix.platform }} static musl binary${{ matrix.debug && ' (debug)' || '' }}${{ matrix.mimalloc && ' (mimalloc)' || '' }}
name: Build ${{ matrix.platform }} static musl binary${{ matrix.debug && ' (debug)' || '' }}
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
needs: [prepare]
steps:
Expand Down Expand Up @@ -141,25 +138,24 @@ jobs:
uses: docker/bake-action@v6
with:
pull: true
load: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug || matrix.mimalloc }}
load: ${{ !fromJson(needs.prepare.outputs.push) || matrix.debug }}
source: .
targets: static-builder-musl
set: |
${{ matrix.debug && 'static-builder-musl.args.DEBUG_SYMBOLS=1' || '' }}
${{ matrix.mimalloc && 'static-builder-musl.args.MIMALLOC=1' || '' }}
${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-musl.args.NO_COMPRESS=1' || '' }}
*.tags=
*.platform=${{ matrix.platform }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-from=type=gha,scope={0}-static-builder-musl{1}{2}', needs.prepare.outputs.ref || github.ref, matrix.debug && '-debug' || '', matrix.mimalloc && '-mimalloc' || '') }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-from=type=gha,scope=refs/heads/main-static-builder-musl{0}{1}', matrix.debug && '-debug' || '', matrix.mimalloc && '-mimalloc' || '') }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-to=type=gha,scope={0}-static-builder-musl{1}{2},ignore-error=true', needs.prepare.outputs.ref || github.ref, matrix.debug && '-debug' || '', matrix.mimalloc && '-mimalloc' || '') }}
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-from=type=gha,scope={0}-static-builder-musl{1}', needs.prepare.outputs.ref || github.ref, matrix.debug && '-debug' || '') }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-from=type=gha,scope=refs/heads/main-static-builder-musl{0}', matrix.debug && '-debug' || '') }}
${{ fromJson(needs.prepare.outputs.push) && '' || format('*.cache-to=type=gha,scope={0}-static-builder-musl{1},ignore-error=true', needs.prepare.outputs.ref || github.ref, matrix.debug && '-debug' || '') }}
${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
name: Export metadata
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
if: fromJson(needs.prepare.outputs.push) && !matrix.debug
run: |
mkdir -p /tmp/metadata

Expand All @@ -169,7 +165,7 @@ jobs:
env:
METADATA: ${{ steps.build.outputs.metadata }}
- name: Upload metadata
if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc
if: fromJson(needs.prepare.outputs.push) && !matrix.debug
uses: actions/upload-artifact@v6
with:
name: metadata-static-builder-musl-${{ steps.prepare.outputs.sanitized_platform }}
Expand All @@ -181,9 +177,9 @@ jobs:
# shellcheck disable=SC2034
# TODO: remove "containerimage.config.digest" fallback once all runners use buildx v0.18+
# which replaced it with "containerimage.digest" and "containerimage.descriptor"
digest=$(jq -r '."static-builder-musl" | ${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && '."containerimage.digest"' || '(."containerimage.config.digest" // ."containerimage.digest")' }}' <<< "${METADATA}")
docker create --platform="${PLATFORM}" --name static-builder-musl "${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc) && '${IMAGE_NAME}@${digest}' || '${digest}' }}"
docker cp "static-builder-musl:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}"
digest=$(jq -r '."static-builder-musl" | ${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug) && '."containerimage.digest"' || '(."containerimage.config.digest" // ."containerimage.digest")' }}' <<< "${METADATA}")
docker create --platform="${PLATFORM}" --name static-builder-musl "${{ (fromJson(needs.prepare.outputs.push) && !matrix.debug) && '${IMAGE_NAME}@${digest}' || '${digest}' }}"
docker cp "static-builder-musl:/go/src/app/dist/${BINARY}" "${BINARY}${{ matrix.debug && '-debug' || '' }}"
env:
METADATA: ${{ steps.build.outputs.metadata }}
BINARY: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}
Expand All @@ -192,12 +188,12 @@ jobs:
if: ${{ !fromJson(needs.prepare.outputs.push) }}
uses: actions/upload-artifact@v6
with:
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}
path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}
compression-level: 0
- name: Upload assets
if: fromJson(needs.prepare.outputs.push) && (needs.prepare.outputs.ref || github.ref_type == 'tag')
run: gh release upload "${REF}" frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} --repo dunglas/frankenphp --clobber
run: gh release upload "${REF}" frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }} --repo dunglas/frankenphp --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REF: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref }}
Expand All @@ -216,7 +212,7 @@ jobs:
"${BINARY}" list-modules | grep http.handlers.vulcain
"${BINARY}" php-cli -r "echo 'Sanity check passed';"
env:
BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }}
BINARY: ./frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}

build-linux-gnu:
permissions:
Expand Down
4 changes: 3 additions & 1 deletion alpine.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,11 @@ ENV GODEBUG=cgocheck=0

# copy watcher shared library (libgcc and libstdc++ are needed for the watcher)
COPY --from=builder /usr/local/lib/libwatcher* /usr/local/lib/
RUN apk add --no-cache libstdc++ && \
RUN apk add --no-cache libstdc++ mimalloc2 && \
ldconfig /usr/local/lib

ENV LD_PRELOAD=/usr/lib/libmimalloc.so.2

COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
RUN setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \
frankenphp version && \
Expand Down
5 changes: 4 additions & 1 deletion build-static.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ os="$(uname -s | tr '[:upper:]' '[:lower:]')"
# - FRANKENPHP_VERSION: FrankenPHP version (default: current Git commit)
# - EMBED: Path to the PHP app to embed (default: none)
# - DEBUG_SYMBOLS: Enable debug symbols if set to 1 (default: none)
# - MIMALLOC: Use mimalloc as the allocator if set to 1 (default: none)
# - MIMALLOC: Use mimalloc as the allocator if set to 1 (default: none, used on musl-linux)
# - XCADDY_ARGS: Additional arguments to pass to xcaddy
# - RELEASE: [maintainer only] Create a GitHub release if set to 1 (default: none)

Expand All @@ -38,6 +38,9 @@ fi
if [ -z "${SPC_LIBC}" ]; then
if [ "${os}" = "linux" ]; then
SPC_LIBC="musl"
if [ -z "${MIMALLOC}" ]; then
MIMALLOC=1
fi
Comment on lines +41 to +43
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose to go this route so that people using the static builder themselves also get mimalloc by default. Other option was to change the default in our matrix.

fi
fi
# init spc build additional args
Expand Down
19 changes: 19 additions & 0 deletions docs/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,25 @@ WORKDIR /app
ENTRYPOINT ["/usr/local/bin/frankenphp", "run", "-c", "/etc/caddy/Caddyfile"]
```

## Mimalloc

The Alpine Linux Docker images use [mimalloc](https://github.com/microsoft/mimalloc) as the default memory allocator.
This improves performance compared to musl's default allocator in threaded environments.

To disable mimalloc, unset the `LD_PRELOAD` environment variable:

```dockerfile
FROM dunglas/frankenphp:alpine

ENV LD_PRELOAD=''
```

Or at runtime:

```console
docker run -e LD_PRELOAD= dunglas/frankenphp:alpine
```

## Development Versions

Development versions are available in the [`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev) Docker repository.
Expand Down
6 changes: 4 additions & 2 deletions docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ especially when compiled in ZTS mode (thread-safe), which is required for Franke

Also, [some bugs only happen when using musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).

In production environments, we recommend using FrankenPHP linked against glibc, compiled with an appropriate optimization level.
To mitigate musl's performance issues in threaded environments, the Alpine Docker images and static binaries use [mimalloc](https://github.com/microsoft/mimalloc) as the default memory allocator.

This can be achieved by using the Debian Docker images, using [our maintainers .deb, .rpm, or .apk packages](https://pkgs.henderkes.com), or by [compiling FrankenPHP from sources](compile.md).
In production environments, we still recommend using FrankenPHP linked against glibc, compiled with an appropriate optimization level.

This can be achieved by using the Debian Docker images, using [our maintainers .deb or .rpm packages](https://pkgs.henderkes.com), or by [compiling FrankenPHP from sources](compile.md).

For leaner or more secure containers, you may want to consider [a hardened Debian image](docker.md#hardening-images) rather than Alpine.

Expand Down
6 changes: 3 additions & 3 deletions docs/static.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ docker buildx bake --load static-builder-musl
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
```

For better performance in heavily concurrent scenarios, consider using the [mimalloc](https://github.com/microsoft/mimalloc) allocator.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain now to disable it instead?

The resulting binary will be using the [mimalloc](https://microsoft.github.io/mimalloc/) allocator for better performance. If you wish to use musl's built-in allocator, set `MIMALLOC=0`:

```console
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=0 static-builder-musl
```

### glibc-Based, Mostly Static Build (With Dynamic Extension Support)
Expand Down Expand Up @@ -126,7 +126,7 @@ script to customize the static build:
- `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
- `NO_COMPRESS`: don't compress the resulting binary using UPX
- `DEBUG_SYMBOLS`: when set, debug-symbols will not be stripped and will be added to the binary
- `MIMALLOC`: (experimental, Linux-only) replace musl's mallocng by [mimalloc](https://github.com/microsoft/mimalloc) for improved performance. We only recommend using this for musl targeting builds, for glibc prefer disabling this option and using [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) when you run your binary instead.
- `MIMALLOC`: (Linux-only, default to `1` for musl) use [mimalloc](https://github.com/microsoft/mimalloc) for improved performance. For glibc, we recommend using [`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html) when you run your binary instead.
- `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub

## Extensions
Expand Down
2 changes: 1 addition & 1 deletion static-builder-musl.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ENV SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES="${XCADDY_ARGS}"
ARG CLEAN=''
ARG EMBED=''
ARG DEBUG_SYMBOLS=''
ARG MIMALLOC=''
ARG MIMALLOC=1
ARG NO_COMPRESS=''

ENV GOTOOLCHAIN=local
Expand Down