Docker
git clone https://github.com/jp1337/easywall.git
cd easywall
docker compose up -d
Open https://localhost:12227 and complete the setup wizard.
Then complete the setup.
./configchanges owner on first start, and needs to. The mount replaces the ownership the image sets, so the files arrive belonging to whoever cloned the repository — andeasywall-webmust writeweb.tomland create its certificate inconfig/ssl/. It could do neither, and the container reported healthy anyway because the healthcheck only looked at the core’s socket. The entrypoint now puts the directory into the shape the Debian package installs. Editing those files on the host afterwards needssudo.
Where to pull from
The same linux/amd64 + linux/arm64 image, pushed to all three by CI. Public, no
authentication.
| Registry | Image |
|---|---|
| GitHub Container Registry | ghcr.io/jp1337/easywall |
| Docker Hub | docker.io/kermit1337/easywall |
| Quay.io | quay.io/jp1337/easywall |
Quay is behind. The publishing token is being rejected, so that mirror is skipped until it is replaced, and it does not have the recent releases. Use GHCR or Docker Hub.
Which tag
| Tag | Moves | For |
|---|---|---|
:latest |
on stable releases only — a release candidate does not move it | production |
:vX.Y.Z |
never | pinning, e.g. v2.7.0 |
:edge |
after every green build on main |
tracking development, demo mode |
:sha-<commit> |
never | rollback and debugging |
# docker-compose.override.yml — pin a version
services:
easywall:
image: ghcr.io/jp1337/easywall:v2.7.0
Why host networking
nftables acts on the host’s network namespace. In its own namespace the container
would filter only its own traffic, which is not what you asked for. NET_ADMIN is
what lets it issue the netlink calls that reach host tables.
network_mode: host
cap_add:
- NET_ADMIN
security_opt:
- no-new-privileges:true
NET_ADMIN and nothing beyond it. The shipped compose file also asked for
SYS_MODULE — the capability to load kernel modules, which from a container that
already shares the host’s network is host root under another name, and which this
page never listed. It is gone; applying a full rule set was verified without it. If
nf_tables is not loaded, load it on the host with modprobe nf_tables — a host
already running nftables has it.
This is also why easywall in a container still coexists with Docker’s own rules —
it owns table inet easywall and nothing else.
Needs
- Docker Engine 24+, Compose v2
- A Linux host with nftables
Your own certificate
volumes:
- /etc/letsencrypt:/etc/letsencrypt:ro
- ./config:/etc/easywall
# config/web.toml
[tls]
cert = "/etc/letsencrypt/live/example.com/fullchain.pem"
key = "/etc/letsencrypt/live/example.com/privkey.pem"
Updating
docker compose pull && docker compose up -d
Watchtower automates it. Nightly or weekly on
:latest for production, or against :edge if you want every green build.
Checking what you pulled
Every image carries the source commit in an OCI label:
docker pull ghcr.io/jp1337/easywall:latest
docker image inspect \
--format '{{ index .Config.Labels "org.opencontainers.image.revision" }}' \
ghcr.io/jp1337/easywall:latest
This used to read
.Manifest.Annotations, where the value has never been. A label goes into the image config; manifest annotations are a different field that only carries base-image and creation keys. Checked by building an image with--label org.opencontainers.image.revision=abc123and reading the pushed OCI layout:manifest annotationsheldimage.base.nameandimage.created, andabc123was inconfig.Labels. So the command printed nothing and looked like an image with no provenance. Release images genuinely had none either — the labels are set in theDockerfilenow, which is the one file all three build paths share.
Compare it against the commit the release tag points at. The workflows that publish
these images are
release.yml
and
publish-edge.yml.