Skip to main content
Nanahoshi configuration is managed entirely through environment variables. Copy .env.example to .env and configure the following:

General

DATABASE_URL

Required — PostgreSQL connection string.
DATABASE_URL=postgresql://postgres:password@localhost:5432/nanahoshi-v2
Format: postgresql://[user]:[password]@[host]:[port]/[database] For Docker Compose, use the service name as host:
DATABASE_URL=postgresql://postgres:password@postgres:5432/nanahoshi-v2

CORS_ORIGIN

Required — Allowed origin for CORS requests (web frontend URL).
CORS_ORIGIN=http://localhost:3001
For production:
CORS_ORIGIN=https://nanahoshi.yourdomain.com

ENVIRONMENT

Optional — Application environment. Defaults to development.
ENVIRONMENT=development  # or production

SERVER_URL

Required — Base URL where the server is accessible.
SERVER_URL=http://localhost:3000
Used for:
  • Generating download links
  • Email template URLs
  • OAuth redirect URIs

NANAHOSHI_DATA_PATH

Optional — Directory for storing covers, thumbnails, and cache. Defaults to ./data.
NANAHOSHI_DATA_PATH=./data
In Docker, this is mounted as a volume at /app/apps/server/data.

NAMESPACE_UUID

Required — Stable UUID namespace for generating deterministic book IDs.
NAMESPACE_UUID=00000000-0000-0000-0000-000000000000
Generate with:
uuidgen
Do not change this after initial setup. Changing it will regenerate all book UUIDs and break existing links.

DOWNLOAD_SECRET

Required — Secret key for signing download URLs.
DOWNLOAD_SECRET=00000000-0000-0000-0000-000000000000
Generate with:
uuidgen

Authentication

BETTER_AUTH_SECRET

Required — Secret key for signing session tokens. Must be at least 32 characters.
BETTER_AUTH_SECRET=change-me-to-a-random-string-at-least-32-chars
Generate with:
openssl rand -hex 32

BETTER_AUTH_URL

Required — Base URL for better-auth callbacks. Must match SERVER_URL.
BETTER_AUTH_URL=http://localhost:3000

DISCORD_CLIENT_ID

Optional — Discord OAuth client ID.
DISCORD_CLIENT_ID=your-client-id
Obtain from the Discord Developer Portal.

DISCORD_CLIENT_SECRET

Optional — Discord OAuth client secret.
DISCORD_CLIENT_SECRET=your-client-secret
Discord OAuth is optional. Users can always register with email/password.

Web (build-time)

VITE_SERVER_URL

Required — URL the browser uses to reach the API server.
VITE_SERVER_URL=http://localhost:3000
This is a build-time variable used by Vite. Changes require rebuilding the web frontend:
docker compose up -d --build web
For production:
VITE_SERVER_URL=https://api.yourdomain.com

SMTP (email)

SMTP configuration for sending verification emails, password resets, and invitations.

SMTP_HOST

Required — SMTP server hostname.
SMTP_HOST=smtp.gmail.com

SMTP_PORT

Required — SMTP server port.
SMTP_PORT=465
Common ports:
  • 465 — SSL/TLS (most common)
  • 587 — STARTTLS
  • 25 — Unencrypted (not recommended)

SMTP_SECURE

Required — Use SSL/TLS connection.
SMTP_SECURE=true
Set to true for port 465, false for 587.

SMTP_USER

Required — SMTP authentication username (usually your email).

SMTP_PASS

Required — SMTP authentication password.
SMTP_PASS=your-app-password
For Gmail, generate an App Password instead of using your account password.

Elasticsearch

ELASTICSEARCH_NODE

Required — Elasticsearch connection URL.
ELASTICSEARCH_NODE=http://127.0.0.1:9200
For Docker Compose:
ELASTICSEARCH_NODE=http://elasticsearch:9200

ELASTICSEARCH_INDEX_PREFIX

Optional — Prefix for Elasticsearch indices. Defaults to nanahoshi.
ELASTICSEARCH_INDEX_PREFIX=nanahoshi
The book index will be named ${prefix}_books (e.g., nanahoshi_books).

Redis

REDIS_HOST

Required — Redis server hostname.
REDIS_HOST=127.0.0.1
For Docker Compose:
REDIS_HOST=redis

REDIS_PORT

Required — Redis server port.
REDIS_PORT=6379

REDIS_PASSWORD

Required — Redis authentication password.
REDIS_PASSWORD=change-me
Generate a strong password for production deployments. Redis is required for BullMQ job queues.

Docker Compose only

These variables are only used in docker-compose.yml and do not affect bare-metal deployments.

POSTGRES_PASSWORD

Required — Password for the PostgreSQL container.
POSTGRES_PASSWORD=password
Must match the password in DATABASE_URL.

SERVER_PORT

Optional — Host port to expose the server on. Defaults to 3000.
SERVER_PORT=3000
The server always listens on port 3000 inside the container. This maps it to a different host port:
ports:
  - "${SERVER_PORT:-3000}:3000"

WEB_PORT

Optional — Host port to expose the web frontend on. Defaults to 3001.
WEB_PORT=3001

Validation

Environment variables are validated at startup using @t3-oss/env-core and Zod in packages/env/src/server.ts. Missing or invalid variables will cause the server to exit with an error. To check your configuration:
bun run dev:server
Or in Docker:
docker compose logs server
Look for validation errors in the startup logs.

Build docs developers (and LLMs) love