Sign inSign up

bfren/nginx

By bfren

โ€ขUpdated about 1 hour ago

Image
0

100K+

bfren/nginx repository overview

โ Nginx

GitHub release (latest by date) Docker Pulls Docker Image Size GitHub Workflow Status

Docker Hubโ  - Quay.ioโ  - bfren ecosystemโ 

โ Introduction

A small, sensible nginxโ  image for serving static files, or as the base for images that need a web server - Nginx + PHPโ , WordPressโ , Baikalโ , FreshRSSโ  and Nginx + WebDAVโ  are all built on it.

It is Alpine + S6โ  with:

  • nginx from Alpine's own packages, with the Brotli module (and zstd, on Alpine 3.20 and above)
  • a ready-made configuration with zstd, Brotli and gzip compression, hidden version number and sensible defaults
  • helper files you can include for static files, error pages, uploads and more
  • log forwarding to docker logs, a daily configuration reload and a health check

There is no SSL support - it is designed to run behind a reverse proxy such as Caddyโ .

โ Quickstart

Serve the files in ./site on port 8080:

docker run -d --name web -p 8080:80 -v ./site:/www/public bfren/nginx

Or with Docker Compose:

services:
  web:
    image: quay.io/bfren/nginx:nginx1.30-alpine3.24-7.3
    ports:
      - "8080:80"
    volumes:
      - ./site:/www/public

Open http://localhost:8080. If you don't mount anything, nginx serves a "Welcome to Nginx" page.

โ Features

  • ๐Ÿชถ Alpine 3.15 to 3.24, each with its own nginx version - choose with the image tag
  • ๐Ÿ—œ๏ธ zstd (Alpine 3.20 and above), Brotli and gzip compression, with pre-compressed .gz and .br files served as they are
  • ๐Ÿ”’ server_tokens off, and nginx workers run as a non-root www user
  • ๐Ÿงฉ Include-based configuration: add your own sites, server snippets, modules and http settings without replacing nginx.conf
  • ๐Ÿ“œ Error log always forwarded to docker logs, access log optionally
  • ๐Ÿ” Configuration tested at start-up, and reloaded every day at 02:00
  • ๐Ÿฉบ Health check that loads a URI of your choice (set BF_NGINX_HEALTHCHECK_URI)
  • ๐Ÿงช Built-in test runner (from Alpineโ )

โ Tags

Images are published to Docker Hub (bfren/nginx) and Quay.io (quay.io/bfren/nginx) for linux/amd64, linux/arm/v7 and linux/arm64. Tags include the nginx version, because the version depends on the Alpine release.

TagExampleUse when
nginxX.Y-alpineA.Bnginx1.30-alpine3.24You want the latest image for an nginx and Alpine version
nginxX.Y-alpineA.B-MAJORnginx1.30-alpine3.24-7You want updates, but not breaking changes
nginxX.Y-alpineA.B-MINORnginx1.30-alpine3.24-7.3You want fixes only - recommended for building on
nginxX.Y-alpineA.B-VERSIONnginx1.30-alpine3.24-7.3.0You want to pin this image exactly
nginxX.Y.Z-alpineA.Bnginx1.30.4-alpine3.24You want an exact nginx release
nginxX.Y.Z-alpineA.B-VERSIONnginx1.30.4-alpine3.24-7.3.0You want to pin both
latest, 7, 7.3, 7.3.0As above, on the newest Alpine version
nginxX.Y, nginxX.Y.Znginx1.30, nginx1.30.4As above, on the newest Alpine version
AlpinenginxAlpine security support ends
3.241.302028-06-01
3.231.282027-11-01
3.221.282027-05-01
3.211.262026-11-01
3.201.26ended 2026-04-01
3.191.24ended 2025-11-01
3.181.24ended 2025-05-09
3.171.22ended 2024-11-22
3.161.22ended 2024-05-23
3.151.20ended 2023-11-01

Images for unsupported Alpine versions are still built, but no longer receive Alpine security fixes - avoid them for anything new.

โ Configuration

โ Ports
PortPurpose
80HTTP - put a reverse proxy in front of it for SSL.
โ Volumes
VolumePurpose
/wwwFiles in /www/public are served by nginx. If /www/public doesn't exist it is created with a welcome page.
โ Environment Variables
VariableValuesDescriptionDefault
BF_NGINX_FORWARD_ACCESS_LOG0, 1Forward the access log to docker logs (the error log is always forwarded).0
BF_NGINX_HEALTHCHECK_URIURIThe URI loaded by the health check.http://localhost
BF_NGINX_IGNORE_FAVICON0, 1Return 204 No Content for a missing favicon.ico, instead of a 404 in the error log.1
BF_NGINX_PUBLIC_OVERRIDEpathServe files from this directory instead of /www/public.blank
BF_DEBUG0, 1Show detailed output from bf scripts.0

Alpine + S6 adds more variablesโ  (timezone, cron).

โ How It Works

โ Starting

When the container starts, Alpine + S6โ  runs the initialisation scripts, then S6 starts three services:

ServiceDoes
nginxTests the configuration, then runs nginx in the foreground.
nginx-error-logForwards /var/log/nginx/error.log to docker logs.
nginx-access-logForwards /var/log/nginx/access.log to docker logs - only if BF_NGINX_FORWARD_ACCESS_LOG=1, otherwise it stops itself.

The image's initialisation scripts (10-env, 11-conf, 12-public) set the nginx environment variables, generate the configuration files below from templates, and create the public directory if it doesn't exist.

If the configuration test fails, nginx doesn't start and the container stops - see docker logs for the error.

โ Configuration files

nginx.conf is generated at start-up from a template, so it picks up your environment variables. You normally don't edit it - add to it using these directories instead:

DirectoryIncluded inUse for
/etc/nginx/modulesthe main contextload_module lines for extra modules.
/etc/nginx/http.dthe http blockSettings that apply to every site, e.g. map blocks or rate limits.
/etc/nginx/sitesthe http blockOne server { โ€ฆ } file per site.
/etc/nginx/customthe default localhost siteExtra directives for the default site, e.g. a location block.

The default site, sites/localhost.conf, is a single server block that serves /www/public on port 80. Add your own .conf files to sites, or map a file over localhost.conf to replace it.

nginx's own /etc/nginx files are owned by the www user and made read-only at start-up (see ch.d in Alpine + S6โ ), so mounted configuration files must be readable by www.

โ Helper files

Include these in a server block with include helpers/<name>; (the path is relative to /etc/nginx):

HelperDescription
base.confGenerated at start-up. Listens on port 80 (IPv4 and IPv6) as the default server and sets the document root.
discworld.confAdds the GNU Terry Pratchettโ  header.
error-pages.confUse nginx's default error page for 500, 502, 503 and 504 errors.
ignore-favicon.confGenerated at start-up. Returns 204 for a missing favicon.ico and keeps it out of the logs - empty if BF_NGINX_IGNORE_FAVICON=0.
static-files.confSets a 365 day expiry, a public cache header and turns off logging for static file types (images, CSS, JavaScript, fonts, video).
uploads.confSets client_max_body_size 0, so there is no limit on upload size.
โ Compression

Text responses are compressed with zstd, Brotli or gzip, depending on what the browser accepts. This happens on the fly for every request - nginx doesn't cache the compressed result. zstd is only installed on Alpine 3.20 and above, because older releases don't have the module; there, responses use Brotli or gzip. Compression levels are 6 for gzip and Brotli, and 3 for zstd. Responses smaller than 256 bytes are not compressed. Only gzip and Brotli have pre-compressed file support (below); zstd is always compressed on the fly.

To avoid that cost for static files, compress them when you build your site: put a .br (brotli -k -q 11 file) and/or a .gz (gzip -k -9 file) next to each file, and nginx serves them directly. brotli_static and gzip_static are both on, and they only take effect when a pre-compressed file exists.

โ Logs

nginx writes to /var/log/nginx/access.log and error.log. The error log is always forwarded to docker logs. Set BF_NGINX_FORWARD_ACCESS_LOG=1 to forward the access log too - it's off by default so that a busy site doesn't flood the container's log.

โ Reloading

nginx-reload tests the configuration and, if it is valid, reloads nginx without dropping connections. It does nothing if nginx isn't running. It runs automatically every day at 02:00 (so, for example, renewed certificates or changed files are picked up), and you can run it yourself:

docker exec <container> nginx-reload
โ Health check

The health check (/etc/bf/health.d/10-nginx.nu) loads BF_NGINX_HEALTHCHECK_URI and passes if the HTTP status is between 200 and 399. Docker runs it every 30 seconds (every 2 seconds during the first 60 seconds, so a new container reports healthy quickly). Use another URI for sites where / isn't a good test - for example, WordPressโ  uses its login page.

โ The bf-nginx module
CommandUse
bf-nginx conf testTest the nginx configuration and report whether it is valid.
bf-nginx reloadTest the configuration, then reload nginx - only if it is running.

Load it with use bf-nginx. The executable nginx-reload is in /usr/bin/bf and on the PATH.

โ Building on this image

FROM quay.io/bfren/nginx:nginx1.30-alpine3.24-7.3

COPY ./overlay /
RUN bf-install

Put site configuration in overlay/etc/nginx/sites, extra settings in overlay/etc/nginx/http.d and overlay/etc/nginx/custom, and any installation steps in overlay/tmp/install. Add a script to overlay/etc/bf/health.d to check your own service as well as Nginx, or replace overlay/etc/bf/health.d/10-nginx.nu to change Nginx's check. See Alpineโ  and Alpine + S6โ  for everything else.

โ Testing

./test.nu        # builds the image and runs /test
./test.nu --all  # every version, in parallel

/test runs this image's tests - the configuration check, the generated nginx.conf (compression settings, and zstd only where the module is installed) and nginx-reload - alongside the base images' tests, and fails if tests don't execute. Run it on more than one Alpine version, e.g. ./test.nu 3.19 and ./test.nu 3.24, as zstd is only installed from 3.20.

โ Troubleshooting

โ The container stops straight after starting

nginx's configuration test failed. Run docker logs <container> - nginx names the file and line. Common causes are a mounted .conf file that www can't read, or a missing include file.

โ I get a 404 for everything

nginx serves /www/public, not /www. Mount your files at /www/public, or set BF_NGINX_PUBLIC_OVERRIDE.

โ Changes to my configuration don't take effect

Run docker exec <container> nginx-reload, or restart the container. nginx.conf, base.conf and ignore-favicon.conf are regenerated from templates every time the container starts, so don't edit them - add to http.d, sites or custom instead.

โ I can't see access logs in docker logs

Set BF_NGINX_FORWARD_ACCESS_LOG=1. They are always written to /var/log/nginx/access.log inside the container.

โ The health check fails

The URI in BF_NGINX_HEALTHCHECK_URI must return a 2xx or 3xx status from inside the container. Test it with docker exec <container> bf-healthcheck.

โ FAQs

โ Why no SSL?

SSL is better handled once, by a reverse proxy in front of all your containers, than in each one. Caddyโ  does it automatically.

โ Can I run PHP?

Use Nginx + PHPโ , which is built on this image and runs PHP-FPM.

โ Why do the tags include the nginx version?

nginx comes from Alpine's packages, so each Alpine release has a different version. The tag tells you exactly which you get, and nginxX.Y.Z-alpineA.B-VERSION pins both.

โ Licence

Copyright (c) 2020-2026 bfrenโ  (unless otherwise stated)

Tag summary

Content type

Image

Digest

sha256:c8b0fa925โ€ฆ

Size

29.4 MB

Last updated

about 1 hour ago

docker pull bfren/nginx