Quick start with Docker
You need Docker Compose and a Cloudflare API token that can edit Pages. Five commands and the panel is running:
mkdir easypages && cd easypages
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/KN990x/EasyPages/main/docker-compose.yml
curl -fsSL -o .env.example https://raw.githubusercontent.com/KN990x/EasyPages/main/.env.example
cp .env.example .env
docker compose up -d --pull always
Edit .env before the last command — CF_API_TOKEN is the only required variable.
There is no git clone in that list on purpose: Compose pulls a pinned image from GHCR, mounts
./easypages-data:/data and health-checks /api/health. Your account and the session secret
live in that volume. The container runs as uid 1000, so if you created the directory as root,
run chown -R 1000:1000 ./easypages-data.
Then open http://your-server:8002.
The Cloudflare API token
Create a custom API token in Cloudflare Dashboard → My Profile → API Tokens with a single permission:
Account→Cloudflare Pages→Edit
That is enough. You do not need to look up your account ID — it is inferred from the token,
exactly as Cloudflare’s own SDK and Wrangler do. Set CF_ACCOUNT_ID only if the token can reach
more than one account, the one case inference cannot resolve. The server logs say so explicitly
and list the IDs to choose from.
Create your account
The first time you open the app you get a setup wizard. Create your account right away.
There is no user or password in .env, and no environment variable can create, replace or
bypass the account. Until the account exists, anyone who can reach the port can create it — so
do not leave a fresh instance exposed and unattended. See
SECURITY.md for the full model.
Environment variables
Only the first one is required. Everything else has a working default.
| Variable | Default | What it does |
|---|---|---|
CF_API_TOKEN | — | Required. Cloudflare API token with Cloudflare Pages: Edit. |
CF_ACCOUNT_ID | inferred | Only needed when the token reaches more than one account. |
EASYPAGES_DATA_DIR | /data | Where the credential file and session secret live. Docker sets this already. |
PORT | 8002 | Port the Node process listens on. |
BIND_HOST | 127.0.0.1 | Interface to bind. The Docker image sets 0.0.0.0 so Compose can publish the port. |
SESSION_COOKIE_SECURE | false | Set to true when HTTPS is terminated in front of the container. |
TRUST_PROXY | one hop | Set to false when Node is reachable without a trusted reverse proxy in front. |
The authoritative list is .env.example.
HTTPS and reverse proxies
Session data lives in a signed cookie (easypages_sid), not in a server-side session
directory — so scaling out does not need sticky sessions.
- Plain HTTP (default):
SESSION_COOKIE_SECURE=false, as shipped. - HTTPS terminated in front: set
SESSION_COOKIE_SECURE=true. - Behind a reverse proxy: keep the default
TRUST_PROXY(one trusted hop for forwarded headers and rate limits). - Node exposed directly: set
TRUST_PROXY=false, otherwise rate limiting can be spoofed through forwarded headers.
Running from source
Use Node.js 24 or newer — the same major as the Docker image and the release CI. With
nvm, nvm use picks it from .nvmrc.
corepack enable
pnpm install --frozen-lockfile
Create .env from .env.example with your Cloudflare token. Local data (credential and session
secret) goes to ./data by default. For a production-like run:
pnpm run build
pnpm start
During active development:
pnpm run dev— Express on port8002(watch mode).pnpm run dev:ui— Vite on5173, proxying/api,/loginand/logouttohttp://localhost:8002.pnpm run lintandpnpm test— ESLint, then the backend (node:test) and frontend (vitest) suites. Runpnpm run buildfirst.
There is a Makefile with the same targets: make setup, make dev, make lint, make test,
make clean-data.
Forgot your password?
There is no reset email. Stop the app, delete the credential file, start again — the wizard comes back:
docker compose down
rm ./easypages-data/credentials.json
docker compose up -d
From a git clone the file is at ./data/credentials.json, or run make clean-data.
Security model in short
- No third-party servers. EasyPages talks straight to the Cloudflare API from your machine.
- Your API token stays in your
.env, next to the container. The operator account is hashed incredentials.jsoninside the data volume. - One operator account, created in the browser, stored hashed in the data volume.
- Signed session cookie, rate-limited login, CSRF tokens on every mutating request.
- Apache-2.0 — read the code, audit it, fork it.
Full detail, including the hardening checklist and known limits, is in SECURITY.md.
Where to go next
- Repository — source, issues and releases.
- CONTRIBUTING.md — repository layout, architectural boundaries and the release process.
- Report a problem — bugs and feature requests.