# Deploy EdgeEver with Docker EdgeEver uses the same web application, Hono routes, services, authentication, MCP implementation, and append-only migrations on Cloudflare and Docker. Only the thin runtime and infrastructure adapters differ: Docker uses Bun with SQLite and local files (or S3-compatible object storage), while Cloudflare uses Workers with D1 and R2. ## Requirements - Docker Engine 24 or later with Docker Compose v2. - An `amd64` or `arm64` Linux host. - Linux kernel 5.6 or later. Older kernels are outside the formally supported Docker environment because Bun may fail to resolve container dependencies there. The installer warns without blocking installation; if startup reports `EISDIR reading .../node_modules/...`, upgrade the host OS or kernel before changing `/data` permissions or replacing application data. - A reverse proxy with HTTPS when the instance is reachable outside a trusted local network. ## One-command install With Docker Compose v2 already installed: ```sh curl -fsSL https://edgeever.org/install.sh | bash ``` The installer creates `~/edgeever`, generates an administrator password, pulls `latest`, starts the container, and waits for it to become healthy. Run the same command again to upgrade without replacing the password or `/data` volume. The installer and Compose configuration use the official GHCR image. By default, the installer schedules `~/edgeever/update.sh` with the current user's crontab at 04:17 server time every day. The updater refreshes the Compose configuration, pulls the configured image tag, restarts the service when needed, and verifies container health. Output is appended to `~/edgeever/update.log`. The default `latest` tag receives new releases automatically; a version supplied with `--version` remains pinned. Use `--no-auto-update` or `EDGE_EVER_AUTO_UPDATE=false` to disable the schedule. If `crontab` is unavailable, the installer keeps `update.sh` and prints instructions for adding it to the NAS task scheduler. Use `--version vX.Y.Z`, `--port PORT`, or `--install-dir DIR` when needed. Run `curl -fsSL https://edgeever.org/install.sh | bash -s -- --help` for all options. ## Manual Compose Download `compose.yaml`, choose the release you want to run, and provide a strong instance password: ```sh export EDGE_EVER_VERSION=vX.Y.Z export EDGE_EVER_AUTH_PASSWORD='replace-with-a-long-random-password' docker compose up -d docker compose ps ``` Open `http://localhost:8787`. The container reports healthy only after the shared `/api/health` endpoint confirms that authentication, SQLite, and object storage are ready. ### Image registry The official image is `ghcr.io/tianma-if/edgeever` and supports `linux/amd64` and `linux/arm64`. Pin `EDGE_EVER_VERSION` to a release tag in production. Compose creates one named volume. Everything that must survive a container replacement is under `/data`: ```text /data/edgeever.sqlite SQLite database /data/edgeever-secrets.json credential encryption secret /data/resources/ local images and attachments ``` The image runs as the non-root `bun` user (UID/GID `1000`). If a NAS requires a host bind mount instead of the named volume, create the directory first and grant UID/GID `1000` read/write access. When an installation or automatic update fails, the script performs a real write test against `/data` and reports the mount type, source, container user, and directory state. If permissions are the cause, it prints a repair command tailored to a Docker named volume or NAS bind mount; it never changes existing data permissions automatically. ## Configuration Common environment variables: | Variable | Default | Purpose | | -------------------------------------- | ------- | ----------------------------------------------------------- | | `EDGE_EVER_AUTH_USERNAME` | `admin` | Initial administrator username | | `EDGE_EVER_AUTH_PASSWORD` | none | Initial password; required for a new database | | `EDGE_EVER_AUTH_PASSWORD_HASH` | none | PBKDF2 hash alternative to the plaintext bootstrap password | | `EDGE_EVER_SESSION_TTL_DAYS` | `400` | Login session lifetime | | `EDGE_EVER_IDLE_TIMEOUT_SECONDS` | `120` | Bun streaming idle timeout, from 10 to 255 seconds | | `EDGE_EVER_CREDENTIALS_ENCRYPTION_KEY` | persisted on `/data` | Optional independent AI credential encryption key. If unset, Docker stores the authentication secret or a generated key in `/data/edgeever-secrets.json`. | | `EDGE_EVER_COMMUNITY_REGISTRY_URL` | unset | Optional HTTPS URL of the signed community plugin catalog. Leave it unset to show only the built-in official plugins. | For secrets, append `_FILE` to a supported variable and point it at a Docker secret, for example `EDGE_EVER_AUTH_PASSWORD_FILE=/run/secrets/auth_password`. The password/hash and S3 access credentials support this form. Do not set both the direct variable and its `_FILE` counterpart. `EDGE_EVER_ALLOW_UNAUTHENTICATED=true` is available for isolated development only. Do not expose an unauthenticated instance to a network. ## S3-compatible attachment storage SQLite remains in `/data`, while new attachments can be stored in MinIO, AWS S3, Alibaba Cloud OSS, Tencent COS, R2, or another compatible service: ```yaml environment: EDGE_EVER_STORAGE_BACKEND: s3 EDGE_EVER_S3_ENDPOINT: https://s3.example.com EDGE_EVER_S3_REGION: us-east-1 EDGE_EVER_S3_BUCKET: edgeever EDGE_EVER_S3_ACCESS_KEY_ID_FILE: /run/secrets/s3_access_key EDGE_EVER_S3_SECRET_ACCESS_KEY_FILE: /run/secrets/s3_secret_key EDGE_EVER_S3_FORCE_PATH_STYLE: "true" ``` Changing the default backend does not migrate historical attachments. Keep the old backend available until its resources have been exported or migrated. ## HTTPS and network exposure The container serves HTTP on port `8787`. Terminate HTTPS in a maintained reverse proxy such as Caddy, Traefik, or Nginx and forward the original host and client address headers. Do not publish SQLite, `/data`, or an object-store administration port. ## Backup and restore Use EdgeEver's ZIP export for portable content backups. Also make a cold copy of the `/data` volume for complete instance recovery: 1. Run `docker compose stop edgeever` and wait for the shutdown-complete log. EdgeEver checkpoints SQLite's WAL during graceful shutdown. 2. Copy or snapshot the entire named volume, including the SQLite file, `edgeever-secrets.json`, and the `resources` directory. 3. Start the service with `docker compose start edgeever`. Docker persists the credential-encryption root in `/data/edgeever-secrets.json`, so a complete `/data` volume backup can decrypt saved AI and object-storage credentials after a container rebuild. Keep any explicit `EDGE_EVER_CREDENTIALS_ENCRYPTION_KEY` in a separate secret backup if you manage secrets outside the volume. Cloudflare deployments still require the Worker authentication secret; they do not use this file. When S3 storage is enabled, back up the bucket separately. Restore only while EdgeEver is stopped, into an empty volume. Test backups periodically on a separate instance. ## Upgrade and rollback Use immutable release tags instead of `latest` for production: ```sh export EDGE_EVER_VERSION=vX.Y.Z docker compose pull docker compose up -d docker compose ps ``` The container applies the same append-only `migrations/*.sql` files used by D1 before it starts accepting traffic. Take a backup first. Application rollback does not reverse a database migration; restore the pre-upgrade volume backup when a data rollback is required. The image default command is `bun scripts/self-hosted-server.js`. It also keeps `scripts/self-hosted-server.mjs` as an alias, because NAS and GUI panels often reuse the command saved from v1.62 and earlier. If logs show `Module not found "scripts/self-hosted-server.mjs"`, pull a release that includes that alias, or clear the custom command so the image default is used. To move between Cloudflare and Docker, use EdgeEver's full backup/export and restore flow. Do not copy a live D1 database file or rewrite migration history. ## Build from source ```sh docker build --tag edgeever:local . docker run --rm -p 8787:8787 \ -e EDGE_EVER_AUTH_PASSWORD='replace-with-a-long-random-password' \ -v edgeever-data:/data \ edgeever:local ``` PostgreSQL is not supported in the first Docker release. It remains a future storage adapter and will not introduce a separate business-code branch.