Ask your question and get a summary of the document by referencing this page and the AI provider of your choice
This doc is out of date, the base version has been updated on 23 August 2026.
Go to English docVersion History
The content of this page was translated using an AI.
See the last version of the original content in EnglishIf you have an idea for improving this documentation, please feel free to contribute by submitting a pull request on GitHub.
GitHub link to the documentationCopy doc Markdown to clipboard
Self-Hosting Intlayer
Intlayer can run entirely on your own infrastructure. No Intlayer Cloud account required. A single command boots a production-ready stack:
One command installs everything:
Copy the code to the clipboard
The installer downloads a docker-compose.yml and a .env, auto-generates the required secrets, and starts all containers with docker compose up -d.
The only external dependency is MongoDB: the backend connects to a MongoDB Atlas cluster, which you provide. Everything else runs inside the container.
Table of Contents
Architecture
Copy the code to the clipboard
Chromium (used for Puppeteer screenshot generation) is bundled inside the backend image. No separate container is needed.
Prerequisites
- Docker ≥ 24 and Docker Compose ≥ v2. If either is missing, the installer prints the install link and exits.
- Ports
3000,3100,8025,9000, and9001available on the host. - A Linux or macOS host (or WSL2 on Windows).
Everything else — Bun, Redis, MinIO, Chromium — ships inside the image.
Quick start
1. Run the installer
Copy the code to the clipboard
It verifies Docker is installed and running, writes ./intlayer.env with BETTER_AUTH_SECRET and S3_SECRET_ACCESS_KEY already generated, and pulls the image. It does not start the container — the backend cannot boot without your database credentials.
Re-running the installer is safe: an existing intlayer.env is never overwritten, so it doubles as the upgrade path.
2. Fill in your credentials
Open intlayer.env and complete the values marked TODO:
Copy the code to the clipboard
The file also carries commented-out blocks for the optional features — SMTP mailer, OPENAI_API_KEY, and the OAuth providers. Uncomment what you need.
The file is read bydocker run --env-file, which does not strip quotes and treats everything after=as the value. Write bare values, and keep comments on their own lines.
3. Start the container
This is the command the installer prints when it finishes:
Copy the code to the clipboard
Then open http://localhost:3000. First boot initialises the datastores, so give it a minute.
The dashboard is served on localhost. See Limitations — custom domains are not supported by the published image.
Installer settings
The installer reads a few environment variables. Because it is piped into sh, pass them to the shell rather than to curl:
Copy the code to the clipboard
Open the table in a modal to view all data content clearly
| Variable | Default | Description |
|---|---|---|
INTLAYER_IMAGE | ghcr.io/aymericzip/intlayer-selfhost:latest | Image to pull |
INTLAYER_ENV_FILE | ./intlayer.env | Where to write the env file |
INTLAYER_CONTAINER_NAME | intlayer | Container name |
INTLAYER_DATA_VOLUME | intlayer-data | Named volume mounted at /data |
INTLAYER_APP_PORT | 3000 | Host port for the dashboard |
INTLAYER_API_PORT | 3100 | Host port for the API |
INTLAYER_S3_PORT | 9000 | Host port for the MinIO S3 API |
INTLAYER_CONSOLE_PORT | 9001 | Host port for the MinIO console |
The four port variables only change the host side of the mapping printed in thedocker runcommand. The published image hashttp://localhost:3000,http://localhost:3100andhttp://localhost:9000compiled into the dashboard bundle at build time, so remapping them leaves the browser pointing at the old ports. Keep the defaults unless you are building your own image — see Limitations.
Quick start
What the installer does:
- Checks that
dockeranddocker composeare present. - Downloads
docker-compose.ymland.env.exampleinto./intlayer/. - If no
.envexists, copies the example and generates random secrets forBETTER_AUTH_SECRET,S3_ACCESS_KEY_ID, andS3_SECRET_ACCESS_KEYviaopenssl rand. - Runs
docker compose pull+docker compose up -d. - Prints the URLs: dashboard
:3000, API:3100, email UI:8025, MinIO console:9001.
After the stack is up, open http://localhost:3000 and create your first account.
Services
Open the table in a modal to view all data content clearly
| Service | Image | Host port(s) | Purpose |
|---|---|---|---|
| app | built from apps/app/Dockerfile | 3000 | TanStack Start dashboard (CMS UI) |
| backend | built from apps/backend/Dockerfile | 3100 | Fastify REST API (/health endpoint) |
| mongo | mongo:7 | internal | Single-node replica set (rs0) |
| redis | redis:7-alpine | internal | Job queues (BullMQ) and caching (ioredis) |
| minio | minio/minio | 9000 (S3), 9001 (console) | S3-compatible object storage for avatars and screenshots |
| mailpit | axllent/mailpit | 1025 (SMTP), 8025 (web UI) | Local transactional email sink |
MinIO port9000must be reachable by the browser because uploaded assets (avatars, screenshots) are loaded directly fromS3_PUBLIC_URL=http://localhost:9000/intlayer.
Environment variables
Required
Open the table in a modal to view all data content clearly
| Variable | Example | Description |
|---|---|---|
DB_ID | intlayer | MongoDB Atlas user |
DB_MDP | (your password) | MongoDB Atlas password |
DB_CLUSTER | cluster0.xxxxx.mongodb.net | MongoDB Atlas cluster host (used in the mongodb+srv:// URI) |
BETTER_AUTH_SECRET | (generated) | 32-byte secret for session signing |
S3_SECRET_ACCESS_KEY | (generated) | Secret for the bundled MinIO |
RESEND_API_KEY | (your key) | Transactional email via Resend. Required for first-run setup unless you configure a global SMTP mailer (see Global mailer) |
Required (auto-generated or prompted)
Open the table in a modal to view all data content clearly
| Variable | Example | Description |
|---|---|---|
NODE_ENV | production | Runtime environment |
PORT | 3100 | Backend listening port |
BACKEND_URL | http://localhost:3100 | Public URL of the backend API |
APP_URL | http://localhost:3000 | Public URL of the dashboard |
DOMAIN | localhost | Cookie domain |
MONGODB_URI | mongodb://mongo:27017/intlayer?replicaSet=rs0 | Full MongoDB connection URI |
REDIS_URL | redis://redis:6379 | Redis connection URL |
BETTER_AUTH_SECRET | (generated) | 32-byte secret for session signing |
MAIL_PROVIDER | smtp | Mail transport: smtp or resend |
MAIL_SMTP_HOST | mailpit | SMTP hostname (Mailpit container name) |
MAIL_SMTP_PORT | 1025 | SMTP port |
MAIL_FROM | Intlayer <no-reply@localhost> | Sender address |
S3_ENDPOINT | http://minio:9000 | S3-compatible endpoint |
S3_PUBLIC_URL | http://localhost:9000/intlayer | Public URL for browser asset loading |
S3_BUCKET_NAME | intlayer | Bucket name |
S3_ACCESS_KEY_ID | (generated) | MinIO access key |
S3_SECRET_ACCESS_KEY | (generated) | MinIO secret key |
VITE_BACKEND_URL | http://localhost:3100 | Backend URL baked into the dashboard at build time |
VITE_DOMAIN | localhost | Domain baked into the dashboard at build time |
Optional (features degrade gracefully when absent)
Open the table in a modal to view all data content clearly
| Variable | Feature |
|---|---|
OPENAI_API_KEY | AI-assisted translation and content audit |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_* | Billing and subscription management |
RESEND_API_KEY | Transactional email via Resend (overrides Mailpit when set) |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | GitHub OAuth login |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Google OAuth login |
GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET | GitLab OAuth login |
MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET | Microsoft OAuth login |
LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET | LinkedIn OAuth login |
ATLASSIAN_CLIENT_ID, ATLASSIAN_CLIENT_SECRET | Atlassian OAuth login |
Global mailer
By default, all transactional emails are sent through Resend using RESEND_API_KEY. Self-hosted deployments can instead route every email — including non-organisation emails such as password resets and magic links — through a global mailer configured with environment variables.
Set MAIL_PROVIDER to activate it. When unset, the default Resend mailer is used.
Open the table in a modal to view all data content clearly
| Variable | Example | Description |
|---|---|---|
MAIL_PROVIDER | smtp | Global transport: smtp or resend. Leave unset to use defaults |
MAIL_FROM | Intlayer <no-reply@acme.com> | Sender header. Accepts a bare address or Name <email> format |
MAIL_SMTP_HOST | smtp.acme.com | SMTP host (required when MAIL_PROVIDER=smtp) |
MAIL_SMTP_PORT | 587 | SMTP port (defaults to 587) |
MAIL_SMTP_SECURE | false | Implicit TLS. Set true for port 465 |
MAIL_SMTP_USER | (your user) | SMTP username (optional; omit for unauthenticated relays) |
MAIL_SMTP_PASSWORD | (your password) | SMTP password |
Precedence: an organisation's own mailer (configured from the Organisation dashboard) takes priority over the global mailer, which in turn takes priority over the default Resend key.
Connecting your Intlayer project
Once the stack is running, point your project at the self-hosted backend and dashboard instead of intlayer.org.
Project configuration
Copy the code to the clipboard
import type { IntlayerConfig } from "intlayer";
const config: IntlayerConfig = {
editor: {
clientId: process.env.INTLAYER_CLIENT_ID,
clientSecret: process.env.INTLAYER_CLIENT_SECRET,
/**
* URL of the self-hosted CMS dashboard.
* Default: https://app.intlayer.org
*/
cmsURL: process.env.INTLAYER_CMS_URL, // e.g. http://localhost:3000
/**
* URL of the self-hosted backend API.
* Default: https://back.intlayer.org
*/
backendURL: process.env.INTLAYER_BACKEND_URL, // e.g. http://localhost:3100
},
};
export default config;
Set the environment variables in your project's .env:
Copy the code to the clipboard
Create access credentials in your self-hosted dashboard under Projects → Access keys at http://localhost:3000/projects.
@intlayer/api SDK
When using the @intlayer/api SDK programmatically, pass backendURL explicitly:
Copy the code to the clipboard
Upgrading
This pulls the latest images and restarts containers with docker compose pull && docker compose up -d. Existing volumes (mongo-data, redis-data, minio-data) are preserved — no data loss.
Copy the code to the clipboard
Backup and restore
All persistent data lives in three named Docker volumes.
Backup
Copy the code to the clipboard
Restore
Copy the code to the clipboard
Limitations
- MongoDB must be external (Atlas). The backend connects only over
mongodb+srv://(built fromDB_ID/DB_MDP/DB_CLUSTER), so a plainmongodb://host:27017— including the container's own bundledmongod— cannot be used. Provide a MongoDB Atlas cluster. - No custom domain. All browser-facing
VITE_*URLs are inlined into the app at build time, and the published image ships withlocalhostvalues. The dashboard must be accessed athttp://localhost:3000; serving it on a public domain would require rebuilding the image with the target URLs baked in and is not supported out of the box. - Email requires a working mailer. First-run setup enforces email verification, so either
RESEND_API_KEYor a global SMTP mailer (MAIL_PROVIDER=smtp+MAIL_SMTP_*) must be configured. After the first admin signs in, each organisation can also configure its own SMTP or Resend mailer from the dashboard.
Troubleshooting
Backend crash-loops on first start
MongoDB and Redis must be healthy before the backend starts. The compose file uses depends_on with condition: service_healthy. If you see repeated backend restarts, check that the mongo and redis healthchecks pass:
Copy the code to the clipboard
Look for MongoDB connection error near the top of the log.
Dashboard cannot reach the API
Verify that VITE_BACKEND_URL matches the URL where the backend is reachable from the browser (not the Docker network). If you changed the backend port or added a reverse proxy, rebuild the dashboard image:
MinIO bucket missing
If the minio-init one-shot service didn't run (or ran before MinIO was ready), create the bucket manually:
Copy the code to the clipboard
