Files
readest/docker/README.md
T
Amir Pourmand 9ad43aa8b2 feat(docker): add GHCR and Docker Hub image publishing (#4250)
* Add GHCR and Docker Hub image publishing with fully runtime-configurable pull-first Docker setup (#1)

* feat: add container image publishing workflow and pull-based compose setup

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/23c31167-9e15-4d44-ab89-f267b8cd6304

* refine docker publishing workflow and pull-first compose docs

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/23c31167-9e15-4d44-ab89-f267b8cd6304

* chore: temporarily expose docker publish run results for pr branch

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/c946a2f2-2219-4dea-a829-61b287bc4859

* fix: update pinned SHAs for setup-qemu-action and setup-buildx-action to v3 heads

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/0db6957e-476b-48e0-acf3-bee6964c3b32

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* chore: switch all workflow action refs from SHA pins to stable version tags

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/19334b37-9b4c-45df-9c3c-81a497cef8e8

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* fix: restore missing `with:` blocks lost during SHA-to-tag substitution

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/f204f742-5b7d-4f05-9647-03b9db86ea3d

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* fix(ci): checkout submodules for docker image workflow

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/72309e8a-6c7c-4004-902a-565f67e5c15f

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* feat(docker): support runtime client env for pulled web image

* refactor(web): serve runtime config via script endpoint

* fix(web): escape runtime config script payload

* fix: align docker runtime config with internal/public backend urls

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/3bbcb608-6202-4f9e-b288-5c95a259da93

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* chore: align published workflow tags with docs

* docs: clarify runtime config precedence and linux host mapping

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/3bbcb608-6202-4f9e-b288-5c95a259da93

* refactor: move storage/quota config from build args to runtime env

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/da9b749e-0b1c-47b9-b474-5009765b6ea6

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* fix: load runtime config in pages router app shell

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/b375132c-8317-4c98-b437-ae48c0153e3d

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* fix: apply biome formatting for runtime config quota helpers

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/778f5d75-884e-401b-b7cb-4ab40bc64a11

Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>
Co-authored-by: Amir Pourmand <pourmand1376@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: S3_PUBLIC_ENDPOINT, _document.tsx for beforeInteractive, runtimeConfig fallbacks, README port (#2)

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/4f92f818-c008-4caa-9684-d530500b5fb2

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

* fix: reformat runtimeConfig.ts to satisfy biome formatter (#3)

Agent-Logs-Url: https://github.com/pourmand1376/readest/sessions/4eea77f4-67ab-4428-b59e-0b21be988037

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pourmand1376 <32064808+pourmand1376@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 19:57:35 +02:00

4.9 KiB

Self-Hosting with Docker/Podman with Compose

Stack

service Image Description
client ghcr.io/readest/readest readest frontend
db supabase/postgres psql db with supabase extensions
kong kong:2.8.1 api gateway routing requests to supabase services
auth supabase/gotrue:v2.185.0 auth service (email, JWT)
rest postgrest/postgrest:v14.3 psql rest api
minio minio/minio s3 storage
minio-setup minio/mc helper container to create s3 buckets

Exposed ports

Port Service
3000 readest
8000 kong API gateway
9000 MinIO S3 API
9001 MinIO console UI

Running with Docker/Podman Compose

1. setup .env

cp docker/.env.example docker/.env

update docker/.env:

  • update POSTGRES_PASSWORD to a strong password (32+ chars)
  • update JWT_SECRET to a random secret (32+ chars)
  • regenerate ANON_KEY and SERVICE_ROLE_KEY as HS256 JWTs signed with your JWT_SECRET (use jwt.io or a similar tool):
    • ANON_KEY payload: {"role": "anon"}
    • SERVICE_ROLE_KEY payload: {"role": "service_role"}
  • set MINIO_ROOT_PASSWORD to a strong password

2. Start the Stack (pull prebuilt client image)

run from the docker/ directory:

cd docker
docker compose up -d

this pulls ${READEST_IMAGE} (default: ghcr.io/readest/readest:latest) instead of building the client locally. the web client now reads SUPABASE_PUBLIC_URL, SUPABASE_ANON_KEY, API_BASE_URL, OBJECT_STORAGE_TYPE, STORAGE_FIXED_QUOTA, and TRANSLATION_FIXED_QUOTA from runtime container env, so custom self-hosted values work with pulled images.

if you prefer Docker Hub, set READEST_IMAGE in docker/.env, for example:

READEST_IMAGE=docker.io/your-dockerhub-username/readest:latest

replace your-dockerhub-username with the Docker Hub namespace that publishes your readest image. for official images, use the namespace configured for this repository's Docker Hub publishing secrets.

published tags:

  • latest: published from release events
  • <release-tag> (for example v1.2.3): published from release events
  • main: rolling image from the default branch
  • sha-<commit>: immutable commit tag

Build locally instead of pulling

cd docker
docker compose -f compose.yaml -f compose.build.yaml up --build -d

3. Access

  • Readest app: http://localhost:3000
  • MinIO console: http://localhost:9001 (login with MINIO_ROOT_USER / MINIO_ROOT_PASSWORD)

Hot Reload (development)

to develop using the compose stack, use local builds (compose.yaml + compose.build.yaml) and set the build target on client to development-stage in compose.build.yaml, which runs the next.js dev server. to enable hot reload, uncomment the volumes block in the client service in compose.yaml:

volumes:
  - ../:/app
  - /app/node_modules
  - /app/apps/readest-app/node_modules
  - /app/apps/readest-app/public/vendor
  - /app/apps/readest-app/.next
  - /app/packages/foliate-js/node_modules

the first mount overlays your local repo into the container. the remaining anonymous volumes shadow the directories that were pre-built inside the image, so the container's installed deps and vendor assets are used instead of what's on your host.

Stop the Stack

cd docker
docker compose down

to also remove volumes (database and storage data):

cd docker
docker compose down -v

Building the Dockerfile standalone

docker build \
  --target production-stage \
  --build-arg NEXT_PUBLIC_APP_PLATFORM=web \
  -t readest-client \
  .

run the built image:

docker run -p 3000:3000 \
  -e SUPABASE_URL=http://host.docker.internal:8000 \
  -e SUPABASE_PUBLIC_URL=http://localhost:8000 \
  -e SUPABASE_ANON_KEY=<anon-key> \
  -e SUPABASE_ADMIN_KEY=<service-role-key> \
  -e API_BASE_URL=http://localhost:3000 \
  -e OBJECT_STORAGE_TYPE=s3 \
  -e S3_ENDPOINT=http://host.docker.internal:9000 \
  -e S3_PUBLIC_ENDPOINT=http://localhost:9000 \
  -e S3_REGION=us-east-1 \
  -e S3_BUCKET_NAME=readest-files \
  -e S3_ACCESS_KEY_ID=<minio-user> \
  -e S3_SECRET_ACCESS_KEY=<minio-password> \
  -e STORAGE_FIXED_QUOTA=1073741824 \
  -e TRANSLATION_FIXED_QUOTA=50000 \
  readest-client

on Linux, some Docker setups do not resolve host.docker.internal by default. in that case, either replace it with your host IP or run with: --add-host=host.docker.internal:host-gateway.