Publish the image the docs have been telling people to pull
README has said `docker run ... ghcr.io/coffey-labs/ihasmail:latest` since the Docker instructions were written, and the docs site repeats it in four places. Nothing ever pushed that image. `docker pull` answers `denied`, because the package does not exist: .github/workflows held ci.yml and nothing else, and there is no reference to ghcr.io, docker/build-push or docker push anywhere in this repo. The instructions have been wrong the whole time. Adds the workflow that makes them true. It fires on a published release, and by hand for a ref -- the same dispatch trigger ci.yml carries, and the only way to build an image for the tags that predate this file. Two architectures on native runners rather than one build under QEMU. Emulated arm64 runs `npm ci` and the Vite build through instruction translation, which takes tens of minutes and sometimes exhausts memory; ubuntu-24.04-arm is free for public repositories and does it at native speed. The cost is pushing by digest and joining the two into one manifest at the end, which is what the third job does. `latest` moves only for a real release. A prerelease that moved it would hand every `:latest` deployment an unfinished build, and a dispatch run has to ask for it deliberately. Also documents the images in README: which tags exist, that the dated tag is the one to pin, and that building it yourself is still fully supported -- `docker compose up --build` is unchanged and the image is a convenience, not a new requirement. Worth knowing before the first run: GHCR creates a new package **private**, even for a public repository, so an anonymous pull will still be refused until the visibility is changed by hand. That is written at the top of the workflow, because it is the failure that looks like success.
This commit is contained in:
@@ -94,6 +94,32 @@ Full instructions, TLS, and every environment variable:
|
||||
[Installing](https://docs.ihasmail.org/install/) ·
|
||||
[Configuring](https://docs.ihasmail.org/configure/).
|
||||
|
||||
### Container images
|
||||
|
||||
Published to GHCR on every release, for `linux/amd64` and `linux/arm64`:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/coffey-labs/ihasmail:latest
|
||||
```
|
||||
|
||||
| Tag | What it is |
|
||||
| --- | --- |
|
||||
| `latest` | The newest release. Prereleases never move it |
|
||||
| `2026.9.2-pr243` | One specific build — the [version](#version-numbers) with `+` written as `-`, because a Docker tag may not contain `+` |
|
||||
|
||||
Pin the dated tag in anything you care about. `latest` is a moving target by
|
||||
definition, and rolling back to a named tag is a `docker run` rather than a
|
||||
rebuild.
|
||||
|
||||
Building it yourself stays fully supported and is what `docker compose up
|
||||
--build` above does — the image is a convenience, not a new requirement. If you
|
||||
build by hand, pass the version in, because `.dockerignore` excludes `.git` and
|
||||
the build cannot work out what it is:
|
||||
|
||||
```bash
|
||||
docker build --build-arg IHASMAIL_VERSION="$(node scripts/version.mjs)" -t ihasmail:local .
|
||||
```
|
||||
|
||||
### Running immutably
|
||||
|
||||
The server writes to exactly one path, the optional `SESSION_FILE`. Clear it
|
||||
|
||||
Reference in New Issue
Block a user