Skip to content
Merged
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
20 changes: 15 additions & 5 deletions .github/workflows/build-containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,39 @@ jobs:
strategy:
matrix:
tag: ["8.2", "8.3", "8.4", "8.5", "8.5-edge"]
mode: ["default", "rootless"]
exclude:
- tag: "8.2"
mode: "rootless"
- tag: "8.5-edge"
mode: "rootless"
steps:
- name: Checkout the codebase
uses: actions/checkout@v6
- name: Build the container for testing
run: make build TAG=$(printenv TAG)
run: make build TAG=$(printenv TAG) MODE=$(printenv MODE)
env:
TAG: ${{ matrix.tag }}
MODE: ${{ matrix.mode }}
- name: Start the container
run: make start TAG=$(printenv TAG) && sleep 10
run: make start TAG=$(printenv TAG) MODE=$(printenv MODE) && sleep 10
env:
TAG: ${{ matrix.tag }}
MODE: ${{ matrix.mode }}
- name: Test the container
run: make test TAG=$(printenv TAG)
run: make test TAG=$(printenv TAG) MODE=$(printenv MODE)
env:
TAG: ${{ matrix.tag }}
MODE: ${{ matrix.mode }}
- name: Login to Docker Hub
if: github.repository_owner == 'eXistenZNL' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build multi-arch and push the container to Docker Hub
if: github.repository_owner == 'eXistenZNL' && github.ref == 'refs/heads/main'
run: make buildx-and-push TAG=$(printenv TAG) MODE=$(printenv MODE)
env:
TAG: ${{ matrix.tag }}
if: github.repository_owner == 'eXistenZNL' && github.ref == 'refs/heads/main'
run: make buildx-and-push TAG=$(printenv TAG)
MODE: ${{ matrix.mode }}
4 changes: 2 additions & 2 deletions 8.2.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM alpine:3.22
FROM alpine:3.22 AS default

LABEL maintainer="docker@stefan-van-essen.nl"

Expand Down Expand Up @@ -29,7 +29,7 @@ RUN case "${TARGETPLATFORM}" in \
*) \
echo "Cannot build, missing valid build platform." \
exit 1; \
esac; \ esac; \
esac; \
rm -rf /tmp/*;

COPY files/general files/php82 /
Expand Down
27 changes: 25 additions & 2 deletions 8.3.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM alpine:3.22
FROM alpine:3.22 AS base

LABEL maintainer="docker@stefan-van-essen.nl"

ARG S6_OVERLAY_VERSION=3.1.6.2
ARG S6_OVERLAY_VERSION=3.2.2.0
ARG TARGETPLATFORM

# Install webserver packages
Expand Down Expand Up @@ -44,6 +44,29 @@ WORKDIR /www

ENTRYPOINT ["/init"]

# =========================================================
# Default mode
# ==========================================================

FROM base AS default

EXPOSE 80

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1/php-fpm-ping || exit 1

# =========================================================
# Rootless mode
# =========================================================

FROM default AS rootless

# Modify configurations and set permissions for rootless operation
RUN sed -i '/^user nginx;/d' /etc/nginx/nginx.conf \
&& sed -i 's|listen 80 default_server;|listen 8080 default_server;|' /etc/nginx/nginx.conf \
&& sed -i '/^user = php$/d; /^group = php$/d' /etc/php83/php-fpm.conf \
&& mkdir -p /var/run/s6 /run/nginx /var/lib/nginx/tmp \
&& chown -R nobody:nobody /var/run/s6 /run /var/lib/nginx /var/log/nginx /www

EXPOSE 8080

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1:8080/php-fpm-ping || exit 1
27 changes: 25 additions & 2 deletions 8.4.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM alpine:3.22
FROM alpine:3.22 AS base

LABEL maintainer="docker@stefan-van-essen.nl"

ARG S6_OVERLAY_VERSION=3.1.6.2
ARG S6_OVERLAY_VERSION=3.2.2.0
ARG TARGETPLATFORM

# Install webserver packages
Expand Down Expand Up @@ -44,6 +44,29 @@ WORKDIR /www

ENTRYPOINT ["/init"]

# =========================================================
# Default mode
# ==========================================================

FROM base AS default

EXPOSE 80

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1/php-fpm-ping || exit 1

# =========================================================
# Rootless mode
# =========================================================

FROM default AS rootless

# Modify configurations and set permissions for rootless operation
RUN sed -i '/^user nginx;/d' /etc/nginx/nginx.conf \
&& sed -i 's|listen 80 default_server;|listen 8080 default_server;|' /etc/nginx/nginx.conf \
&& sed -i '/^user = php$/d; /^group = php$/d' /etc/php84/php-fpm.conf \
&& mkdir -p /var/run/s6 /run/nginx /var/lib/nginx/tmp \
&& chown -R nobody:nobody /var/run/s6 /run /var/lib/nginx /var/log/nginx /www

EXPOSE 8080

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1:8080/php-fpm-ping || exit 1
4 changes: 2 additions & 2 deletions 8.5-edge.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM alpine:3.22
FROM alpine:3.22 AS default

LABEL maintainer="docker@stefan-van-essen.nl"

ARG S6_OVERLAY_VERSION=3.1.6.2
ARG S6_OVERLAY_VERSION=3.2.2.0
ARG TARGETPLATFORM

# Install webserver packages
Expand Down
25 changes: 24 additions & 1 deletion 8.5.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM alpine:3.23
FROM alpine:3.23 AS base

LABEL maintainer="docker@stefan-van-essen.nl"

Expand Down Expand Up @@ -44,6 +44,29 @@ WORKDIR /www

ENTRYPOINT ["/init"]

# =========================================================
# Default mode
# ==========================================================

FROM base AS default

EXPOSE 80

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1/php-fpm-ping || exit 1

# =========================================================
# Rootless mode
# =========================================================

FROM default AS rootless

# Modify configurations and set permissions for rootless operation
RUN sed -i '/^user nginx;/d' /etc/nginx/nginx.conf \
&& sed -i 's|listen 80 default_server;|listen 8080 default_server;|' /etc/nginx/nginx.conf \
&& sed -i '/^user = php$/d; /^group = php$/d' /etc/php85/php-fpm.conf \
&& mkdir -p /var/run/s6 /run/nginx /var/lib/nginx/tmp \
&& chown -R nobody:nobody /var/run/s6 /run /var/lib/nginx /var/log/nginx /www

EXPOSE 8080

HEALTHCHECK --interval=5s --timeout=5s CMD curl -f http://127.0.0.1:8080/php-fpm-ping || exit 1
26 changes: 22 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# Variables
PROJECTNAME=existenz/webstack
TAG=UNDEF
MODE=default
PHP_VERSION=$(shell echo "$(TAG)" | sed -e 's/-.*//')

.PHONY: all
all: build start test stop clean

build:
if [ "$(TAG)" = "UNDEF" ]; then echo "Please provide a valid TAG" && exit 1; fi
docker build -t $(PROJECTNAME):$(TAG) --build-arg="BUILDPLATFORM=linux/amd64" -f $(TAG).Dockerfile --pull .
@if [ "$(MODE)" = "default" ]; then \
docker build -t $(PROJECTNAME):$(TAG) --target $(MODE) --build-arg="BUILDPLATFORM=linux/amd64" -f $(TAG).Dockerfile --pull .; \
else \
docker build -t $(PROJECTNAME):$(TAG)-$(MODE) --target $(MODE) --build-arg="BUILDPLATFORM=linux/amd64" -f $(TAG).Dockerfile --pull .; \
fi

buildx-and-push:
if [ "$(TAG)" = "UNDEF" ]; then echo "Please provide a valid TAG" && exit 1; fi
docker buildx create --use
docker buildx build --platform=linux/amd64,linux/arm64 -f $(TAG).Dockerfile -t $(PROJECTNAME):$(TAG) . --push
@if [ "$(MODE)" = "default" ]; then \
docker buildx build --platform=linux/amd64,linux/arm64 --target $(MODE) -f $(TAG).Dockerfile -t $(PROJECTNAME):$(TAG) . --push; \
else \
docker buildx build --platform=linux/amd64,linux/arm64 --target $(MODE) -f $(TAG).Dockerfile -t $(PROJECTNAME):$(TAG)-$(MODE) . --push; \
fi
docker buildx stop

start:
if [ "$(TAG)" = "UNDEF" ]; then echo "please provide a valid TAG" && exit 1; fi
docker run -d -p 8080:80 --name existenz_webstack_instance $(PROJECTNAME):$(TAG)
@if [ "$(MODE)" = "rootless" ]; then \
docker run -d -p 8080:8080 --user nobody --name existenz_webstack_instance $(PROJECTNAME):$(TAG)-rootless; \
elif [ "$(MODE)" = "default" ]; then \
docker run -d -p 8080:80 --name existenz_webstack_instance $(PROJECTNAME):$(TAG); \
fi

stop:
docker stop -t0 existenz_webstack_instance || true
Expand All @@ -26,7 +40,11 @@ stop:
clean:
if [ "$(TAG)" = "UNDEF" ]; then echo "please provide a valid TAG" && exit 1; fi
rm -rf files/s6-overlay || true
docker rmi $(PROJECTNAME):$(TAG) || true
@if [ "$(MODE)" = "default" ]; then \
docker rmi $(PROJECTNAME):$(TAG) || true; \
else \
docker rmi $(PROJECTNAME):$(TAG)-$(MODE) || true; \
fi

test:
if [ "$(TAG)" = "UNDEF" ]; then echo "please provide a valid TAG" && exit 1; fi
Expand Down
60 changes: 46 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,35 @@ You can create your own containers based upon this container with a simple FROM
### Before you start

Before start hacking away, you should know this:
- Nginx runs under the system's nginx user, and PHP-FPM runs under the system's php user.
- The code should be copied into /www, as this is the default directory Nginx and PHP work with in this container.
- When not using a CMS or framework like Laravel / Symfony / WordPress that brings its own public folder, copy to /www/public instead.
- By default, Nginx runs under the system's nginx user, and PHP-FPM runs under the system's php user. For rootless, see below.
- The code should be copied into `/www`, as this is the default directory Nginx and PHP work with in this container.
- When not using a CMS or framework like Laravel / Symfony / WordPress that brings its own public folder, copy to `/www/public` instead.
- Any PHP modules needed in your project should be installed by using apk, Alpine Linux's package manager and the package names for installing can be looked up in the version table below.

Then there are some tips or rather guidelines that I adhere to personally, but ultimately this is just a matter of taste:
- [S6-overlay can set permissions when the container starts up](https://github.com/just-containers/s6-overlay#fixing-ownership--permissions), but this can be slow if a lot of permissions need to be set, so just do this when building the container.

### Rootless mode

Starting from **PHP 8.3**, this container supports rootless mode for enhanced security.

#### What is rootless mode?

In rootless mode, both Nginx and PHP-FPM run as the same non-root user (typically `nobody`). This eliminates the need
for root privileges inside the container, reducing the attack surface. The container is configured to:
- Run on port 8080 instead of port 80 (non-privileged port)
- Remove user directives from Nginx and PHP-FPM configurations
- Set appropriate permissions for directories that need write access

#### Running rootless containers

When running rootless containers, you must specify a non-root user for the user id. Permissions to various folders have
been set to the `nobody` user (id `65534`) so it's easiest to go with that. If you want to use a different user id, make
sure to set the correct owner on certain folders when building the container. For specifics, see the rootless target
in any Dockerfile.

Also the port is no longer on port `80` because that's a privileged port. Instead, the port has been set to `8080`.

### Basic example

Now that we know all that, we can do something like this:
Expand All @@ -54,25 +75,36 @@ RUN find /www -type d -exec chmod -R 555 {} \; \
```
And you should now have a working container that runs your PHP project!

Or, when picking a rootless container:

```
FROM existenz/webstack:8.5-rootless

COPY --chown=nobody:nobody src/ /www
```

### Versions

> Tags ending with a `-description` install packages from different repositories to keep up with the latest PHP
> versions. These are probably short-lived and will be replaced with their default counterpart as soon as these PHP
> versions make it into the default Alpine repositories. You can use them, just keep in mind you will have to switch
> over to the default container at one point.
> Tags ending with a `-edge` install packages from the edge repository to keep up with the latest PHP versions. These
> are probably short-lived and will be replaced with their default counterpart as soon as these PHP versions make it
> into the default Alpine repositories. You can use them, just keep in mind you will have to switch over to the default
> container at one point.
>
> Codecasts containers are no longer provided, see [this issue](https://github.com/codecasts/php-alpine/issues/131) for
> more information.

See the table below to see what versions are currently available:

| Image tag | Based on | PHP Packages from | S6-Overlay |
|-----------|-------------------|-------------------------------------------------------------------------------------------------|------------|
| 8.2 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php82*&branch=v3.22&arch=x86_64) | Version 1 |
| 8.3 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php83*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.4 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php84*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.5 | Alpine Linux 3.23 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php85*&branch=v3.23&arch=x86_64) | Version 3 |
| 8.5-edge | Alpine Linux Edge | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php85*&branch=edge&arch=x86_64) | Version 3 |
| Image tag | Based on | PHP Packages from | S6-Overlay |
|--------------|-------------------|-------------------------------------------------------------------------------------------------|------------|
| 8.2 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php82*&branch=v3.22&arch=x86_64) | Version 1 |
| 8.3 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php83*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.3-rootless | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php83*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.4 | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php84*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.4-rootless | Alpine Linux 3.22 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php84*&branch=v3.22&arch=x86_64) | Version 3 |
| 8.5 | Alpine Linux 3.23 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php85*&branch=v3.23&arch=x86_64) | Version 3 |
| 8.5-rootless | Alpine Linux 3.23 | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php85*&branch=v3.23&arch=x86_64) | Version 3 |
| 8.5-edge | Alpine Linux Edge | [Alpine Linux repo](https://pkgs.alpinelinux.org/packages?name=php85*&branch=edge&arch=x86_64) | Version 3 |

### Overriding or extending the configuration

Expand Down
Loading