Ask your question and get a summary of the document by referencing this page and the AI provider of your choice
If 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 on your own infrastructure, no Intlayer Cloud account required. Three setups are available, all driven by the same installer (install.sh, install.ps1 on Windows, or npx intlayer init infra):
Open the table in a modal to view all data content clearly
| Setup | What it is | Pick it for |
|---|---|---|
| Desktop app | Native dashboard for macOS, Linux and Windows | A local client, nothing to host |
| All-in-one Docker | Dashboard, API, MongoDB, Redis and MinIO in a single container | Trials and small single-box installs |
| Docker Compose | One container per service, each datastore replaceable by a managed offering | Production, scaling, managed datastores |
Table of Contents
Published images and packages
Open the table in a modal to view all data content clearly
| Artifact | Docker Hub | GHCR mirror | Contents |
|---|---|---|---|
| All-in-one container | intlayer/cms-all | ghcr.io/aymericzip/intlayer/cms-all | app + backend + MongoDB 8 + Redis + MinIO + Chromium |
| Dashboard (frontend) | intlayer/cms-frontend | ghcr.io/aymericzip/intlayer/cms-frontend | TanStack Start dashboard on Bun |
| API (backend) | intlayer/cms-backend | ghcr.io/aymericzip/intlayer/cms-backend | Fastify REST API on Bun + Chromium |
| Desktop app | GitHub releases | n/a | .dmg (macOS), .deb / .rpm / .AppImage (Linux), .exe / .msi (Windows) |
All three images are built from the same docker/selfhost/Dockerfile and published on every release. The Compose stack also pulls the official mongo:8, redis:8-alpine and quay.io/minio/minio images.
Setup
The installer asks which setup you want, checks the prerequisites (offering to install Docker), writes the environment file with the secrets already generated, and pulls the images. It never starts anything on its own: the Docker modes need a mailer first, so it ends by printing the command to run. Re-running it is safe: an existing environment file is never overwritten, which makes it the upgrade path too.
The Intlayer dashboard as a native application, built with Tauri. It signs in to the Intlayer Cloud (https://app.intlayer.org), so there is nothing to host. It is the right choice when you want a local client rather than a browser tab.
Install
The installer downloads the package for your OS and CPU, then opens it (macOS), installs it (dpkg / rpm on Linux) or launches the setup wizard (Windows). You can also download it by hand from the releases page.
Copy the code to the clipboard
In PowerShell:
Copy the code to the clipboard
Copy the code to the clipboard
Requirements
- Node.js: the app embeds the dashboard's server and starts it with the machine's own
nodebinary. Install it from nodejs.org if the app does not start.
The published desktop build talks to the Intlayer Cloud backend. Pointing it at a self-hosted backend requires rebuilding the app with VITE_BACKEND_URL set to your API, see Limitations.
Everything runs inside the single intlayer/cms-all container, supervised by s6-overlay, with every datastore persisted under one volume.
Copy the code to the clipboard
Open the table in a modal to view all data content clearly
| Service | Host port(s) | Purpose |
|---|---|---|
| app | 3000 | Dashboard (CMS UI) |
| backend | 3100 | REST API (/health endpoint) |
| mongo | internal | MongoDB 8, single-node replica set rs0 |
| redis | internal | Job queues (BullMQ) and caching |
| minio | 9000 (S3), 9001 (console) | S3-compatible object storage for avatars and screenshots |
Boot order is enforced by s6 dependencies (mongod → replica-set init, minio → bucket creation, then backend, then app), and services restart on exit, so the first boot recovers on its own.
Prerequisites
- Docker ≥ 24: the installer offers to install it (via get.docker.com on Linux, Homebrew on macOS). On Windows, install Docker Desktop (WSL 2 backend) first.
- Ports
3000,3100,9000and9001free on the host. MinIO9000must stay reachable by the browser, which loads assets straight fromS3_PUBLIC_URL. - A mailer: a Resend API key or an SMTP relay.
1. Install
Writes ./intlayer.env with BETTER_AUTH_SECRET and S3_SECRET_ACCESS_KEY generated, asks a few questions to fill in the rest, and pulls intlayer/cms-all:latest.
Copy the code to the clipboard
In PowerShell:
Copy the code to the clipboard
Copy the code to the clipboard
2. Answer the setup questions
The installer asks for (press Enter to accept a suggestion, every answer can be changed in the file later):
- The domain Intlayer is served on. Leave it empty to stay on
localhost. With a domain such asexample.org, it suggestshttps://cms.example.orgfor the dashboard,https://back.example.orgfor the API andhttps://s3.example.org/intlayerfor the object storage, and writesDOMAIN,APP_URL,BACKEND_URLandS3_PUBLIC_URL. See Custom domain for what follows. - The mailer: Resend (API key) or an SMTP relay (host, port, credentials), plus the sender address. This can be skipped and done by hand later.
- An optional OpenAI API key for the AI features.
Without a terminal (for instance when the script is run from CI), the questions are skipped and only the secrets are generated. Open intlayer.env and fill in Resend or SMTP by hand (details in Global mailer):
Copy the code to the clipboard
3. Start
This is the command the installer prints (with a custom domain, it is preceded by the docker build that produces intlayer/cms-all:custom, see Custom domain):
Copy the code to the clipboard
Copy the code to the clipboard
The CLI runs the installer, which prints the docker run … command shown in the other tabs. Copy it into your terminal once the mailer is configured.
Open http://localhost:3000 (or your dashboard URL) and follow First-run setup. The first boot initialises the replica set and the bucket, so give it a minute.
Backup and upgrade
All state lives in the intlayer-data volume (/data/mongo, /data/redis, /data/minio).
Copy the code to the clipboard
To upgrade, re-run the installer (it pulls the latest image and keeps intlayer.env), then docker rm -f intlayer and run the start command again. To use a managed MongoDB instead of the bundled one, set MONGODB_URI in intlayer.env.
One container per service on a private Compose network. The dashboard and the API use the published intlayer/cms-frontend and intlayer/cms-backend images; the datastores use the official mongo, redis and minio images.
Copy the code to the clipboard
Open the table in a modal to view all data content clearly
| Service | Image | Role |
|---|---|---|
app | intlayer/cms-frontend | Dashboard on :3000; waits for the backend to be healthy |
backend | intlayer/cms-backend | API on :3100 with Chromium; waits for Mongo, Redis and the MinIO bucket |
mongo | mongo:8 | Single-node replica set rs0, initiated by its own healthcheck |
redis | redis:8-alpine | Queues and caching, append-only persistence |
minio | quay.io/minio/minio | S3 storage on :9000, console on :9001 |
minio-init | quay.io/minio/mc | One-shot: creates the bucket and its anonymous-download policy |
Data is kept in the intlayer_mongo-data, intlayer_redis-data and intlayer_minio-data volumes. The service wiring (MONGODB_URI, REDIS_URL, S3_ENDPOINT, the internal backend URL used by server-side rendering) is fixed in the compose file and takes precedence over .env, which only carries secrets and optional integrations.
Prerequisites
- Docker ≥ 24 with the Compose plugin: the installer offers to install it on Linux and macOS. On Windows, install Docker Desktop (WSL 2 backend) first.
- Ports
3000,3100,9000and9001free on the host. - A mailer: a Resend API key or an SMTP relay.
1. Install
Writes docker-compose.yml and a .env with the secrets generated into ./intlayer/, asks the same setup questions as the all-in-one mode (domain, mailer, OpenAI key), and pulls the images.
Copy the code to the clipboard
Or by hand:
Copy the code to the clipboard
In PowerShell:
Copy the code to the clipboard
Or by hand:
Copy the code to the clipboard
Copy the code to the clipboard
2. Configure a mailer
If you skipped the mailer question, fill in Resend or SMTP in intlayer/.env, exactly as for the all-in-one container (see Global mailer).
3. Start
Copy the code to the clipboard
With a custom domain, the installer also downloads docker-compose.build.yml and the start command becomes docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build (see Custom domain).
Open http://localhost:3000 (or your dashboard URL) and follow First-run setup.
Managed datastores
Delete the service you are replacing from the compose file (and its depends_on entry on backend), then override the matching variable:
Copy the code to the clipboard
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY / S3_BUCKET_NAME keep their meaning against any S3-compatible provider.
Scaling
app and backend are stateless. Behind a load balancer, docker compose up -d --scale backend=3 works once the fixed host port mappings are removed and the proxy addresses the services by name. Background jobs are coordinated through Redis (BullMQ), so several backend replicas share the queue safely.
Building from source
An override switches the two Intlayer services from image: to build:. From a checkout of the repository:
Copy the code to the clipboard
Without a checkout, point the build context at the repository itself by setting INTLAYER_BUILD_CONTEXT=https://github.com/aymericzip/intlayer.git#main in .env. The dashboard's VITE_* build args follow DOMAIN, APP_URL and BACKEND_URL from the same file, which is how a custom domain is applied.
Backup and upgrade
Copy the code to the clipboard
Installer settings
Without --mode (or INTLAYER_MODE), the installer shows a menu: desktop, docker (all-in-one) or compose. It also reads a few environment variables. Because it is piped into the shell, pass them to the shell rather than to curl:
Copy the code to the clipboard
Copy the code to the clipboard
Open the table in a modal to view all data content clearly
| Variable | Default | Applies to | Description |
|---|---|---|---|
INTLAYER_MODE | (asked) | all | desktop, docker or compose, same as --mode |
INTLAYER_DOWNLOAD_DIR | ~/Downloads | desktop | Where the app installer is saved |
INTLAYER_IMAGE | intlayer/cms-all:latest | docker | All-in-one image to pull |
INTLAYER_ENV_FILE | ./intlayer.env | docker | Where to write the environment file |
INTLAYER_CONTAINER_NAME | intlayer | docker | Container name |
INTLAYER_DATA_VOLUME | intlayer-data | docker | Named volume mounted at /data |
INTLAYER_APP_PORT | 3000 | docker | Host port for the dashboard |
INTLAYER_API_PORT | 3100 | docker | Host port for the API |
INTLAYER_S3_PORT | 9000 | docker | Host port for the MinIO S3 API |
INTLAYER_CONSOLE_PORT | 9001 | docker | Host port for the MinIO console |
INTLAYER_COMPOSE_DIR | ./intlayer | compose | Where docker-compose.yml and .env are written |
INTLAYER_SELFHOST_REF | main | both | Git ref the compose file and env template are fetched from |
INTLAYER_BUILD_CONTEXT | …/intlayer.git#main | both | Build context used when a custom domain requires a rebuild |
INTLAYER_CUSTOM_IMAGE | intlayer/cms-all:custom | docker | Tag of the all-in-one image built for a custom domain |
The port variables only change the host side of the mapping. The published images havehttp://localhost:3000,http://localhost:3100andhttp://localhost:9000compiled into the dashboard bundle, so remapping them leaves the browser pointing at the old ports. Keep the defaults unless you build your own images, see Limitations.
First-run setup
On a fresh instance (empty database), opening the dashboard redirects you to the /init page:
- Create the first account. Because the users collection is empty, this account is automatically promoted to super admin.
- A verification email is sent through Resend or your SMTP relay. Email verification is mandatory, this is why a mailer must be configured before you start.
- Click the link in the email, then sign in.
Once an admin exists, /init redirects to the standard sign-in page.
Environment variables
Both Docker modes read the same file (intlayer.env for the container, .env for Compose), generated from docker/selfhost/.env.template.
Required
Open the table in a modal to view all data content clearly
| Variable | Example | Description |
|---|---|---|
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 an SMTP relay is configured instead (see Global mailer) |
Fixed by the deployment
These are set by the image (all-in-one) or by the compose file, and only need overriding for a non-standard topology. DOMAIN, APP_URL, BACKEND_URL and S3_PUBLIC_URL are the exception: set in the env file, they take precedence in both modes (see Custom domain).
Open the table in a modal to view all data content clearly
| Variable | All-in-one | Docker Compose | Description |
|---|---|---|---|
PORT | 3100 | 3100 | Backend listening port |
APP_URL | http://localhost:3000 | http://localhost:3000 | Public URL of the dashboard |
BACKEND_URL | http://localhost:3100 | http://localhost:3100 | Public URL of the backend API |
DOMAIN | localhost | localhost | Cookie domain |
SELF_HOSTED | true | true | Disables the cloud-only API endpoints (billing, subscriptions, marketplace) |
MONGODB_URI | mongodb://127.0.0.1:27017/intlayer?replicaSet=rs0 | mongodb://mongo:27017/… | MongoDB connection string, any mongodb:// or mongodb+srv:// cluster works |
REDIS_URL | redis://127.0.0.1:6379 | redis://redis:6379 | Redis |
S3_ENDPOINT | http://127.0.0.1:9000 | http://minio:9000 | MinIO (server-to-server) |
S3_PUBLIC_URL | http://localhost:9000/intlayer | http://localhost:9000/intlayer | Public URL for browser asset loading |
S3_BUCKET_NAME | intlayer | intlayer | Bucket name |
S3_ACCESS_KEY_ID | intlayer | intlayer | MinIO access key |
The Compose app service additionally receives INTLAYER_BACKEND_INTERNAL_URL=http://backend:3100: the browser reaches the API on localhost:3100, but server-side rendering runs inside the Compose network and must use the service name.
Custom domain
The backend reads its public URLs at runtime, but the dashboard has them compiled in: the published intlayer/cms-frontend and intlayer/cms-all images only work on http://localhost:3000. Serving Intlayer on your own domain therefore takes two things, both prepared by the installer when you answer the domain question:
Four variables in the env file, read by the backend (cookies, email links, OAuth callbacks, asset URLs) and used as build args by
docker-compose.build.yml:intlayer.envCopy codeCopy the code to the clipboard
A dashboard image built with those URLs. Docker builds it straight from the repository, no checkout needed:
shCopy codeCopy the code to the clipboard
Then put a reverse proxy with TLS in front of the container: cms.example.org → port 3000, back.example.org → 3100, s3.example.org → 9000. The three hosts must share the DOMAIN suffix, since the session cookie is scoped to it.
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 |
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 |
Global mailer
Every transactional email, including non-organization emails such as password resets and magic links, goes through one of two global transports:
- Resend, using
RESEND_API_KEY. - SMTP, using the
MAIL_SMTP_*variables. As soon asMAIL_SMTP_HOSTis set, SMTP is used andRESEND_API_KEYis ignored.
MAIL_PROVIDER is only needed to force one transport when both are configured (for instance MAIL_PROVIDER=resend to keep Resend while an SMTP host is present).
Open the table in a modal to view all data content clearly
| Variable | Example | Description |
|---|---|---|
MAIL_FROM | Intlayer <no-reply@acme.com> | Sender header for either transport. Accepts a bare address or Name <email> |
MAIL_SMTP_HOST | smtp.acme.com | SMTP host. Setting it selects the SMTP transport |
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 |
MAIL_PROVIDER | resend | Optional override: smtp or resend. Leave unset to auto-select |
Precedence: an organization's own mailer (configured from the Organization 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
Limitations
- A custom domain means a rebuild. All browser-facing
VITE_*URLs are inlined into the dashboard at build time, and the published images (and the desktop app) ship withlocalhost/ Intlayer Cloud values. Out of the box the dashboard must be accessed athttp://localhost:3000, the API at:3100and MinIO at:9000; remapping the host ports has the same effect. The installer wires everything for a rebuild from the repository when you give it a domain (see Custom domain), but the build itself takes several minutes. Pointing the desktop app at a self-hosted backend is not supported. - Email requires a working mailer. First-run setup enforces email verification, so either
RESEND_API_KEYor an SMTP relay (MAIL_SMTP_*) must be configured. After the first admin signs in, each organization can also configure its own SMTP or Resend mailer from the dashboard. - The desktop app needs Node.js on the machine to start its embedded server.
- No documentation assistant. The AI doc assistant of intlayer.org (
/api/ai/ask,/api/search/doc) relies on ~130 MB of pre-computed documentation embeddings that the self-host images do not ship; those two routes are not registered in self-hosted mode. The dashboard's own AI features (translation, audit, autocomplete, chat) are unaffected and only needOPENAI_API_KEY.
Useful links
- Intlayer CMS documentation
- Configuration reference
- CMS SDK:
@intlayer/api - Desktop app releases
- Docker Hub:
intlayer/cms-all,intlayer/cms-frontend,intlayer/cms-backend, mirrored on GHCR underghcr.io/aymericzip/intlayer/ docker/selfhost/: Dockerfile,docker-compose.ymland.env.template
