diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..0925588b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,13 @@
+node_modules
+.next
+.git
+data
+dist
+build
+out
+.turbo
+.vercel
+electron/dist
+*.log
+.env
+.env.local
diff --git a/.env.example b/.env.example
new file mode 100644
index 00000000..1dbef8fa
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,55 @@
+# Claudable server config — copy to .env on your host (DO NOT COMMIT).
+
+# --- Agent auth: run claude -p with your Claude subscription (no API billing) ---
+# Generate once with `claude setup-token` on a logged-in machine, paste here.
+CLAUDE_CODE_OAUTH_TOKEN=
+
+# --- Git provider: 'github' (default) or 'gitea' (self-hosted) ---
+GIT_PROVIDER=github
+GIT_API_BASE_URL=https://git.example.com/api/v1
+GIT_HTTP_BASE=https://git.example.com
+GIT_ORG=your-org
+GIT_DEPLOY_DOMAIN=example.com
+# Git API token (write:repository, write:organization). Used for repo create + push.
+GIT_TOKEN=
+
+# --- App / public URL ---
+DATABASE_URL=file:../data/cc.db
+PROJECTS_DIR=./data/projects
+WEB_PORT=3700
+# Public URL of this Claudable instance (behind your reverse proxy).
+NEXT_PUBLIC_APP_URL=https://claudable.example.com
+# Template for preview URLs. Prefer {project} (a STABLE per-project subdomain whose
+# Traefik route is rewritten to the live port each start — a reused port can never
+# cross projects). Requires TRAEFIK_DYNAMIC_DIR (below) mounted + writable. The
+# legacy {port} form works too but is port-keyed (guessable, cross-project risk).
+PREVIEW_URL_TEMPLATE=https://preview-{project}.example.com
+# How your reverse proxy reaches host-network containers (host gateway IP, or
+# host.docker.internal on Docker Desktop).
+DEPLOY_HOST_GATEWAY=host.docker.internal
+
+# --- Auth (Google OAuth via Auth.js) ---
+# Create an OAuth client at console.cloud.google.com (Web app), redirect URI:
+# https://