Production
A production frontend is two commands run against the same backend URL: ajs dms build writes the build into the backend's workspace, and ajs dms start serves it. The backend itself is deployed separately; see Deployment in the DMS guide.
Deploy
Set the configuration
Put the values in the environment of the machine or container, or in the .env of the directory you run the commands from:
DMS_API_BASE_URL=https://api.example.com
DMS_CLIENT_BASE_URL=https://dms.example.com
DMS_BOOTSTRAP_SECRET=the backend's frontend.bootstrapSecret
DMS_SESSION_SECRET=output of openssl rand -hex 32
DMS_BOOTSTRAP_SECRET must match the backend's frontend.bootstrapSecret: without it, the manifest arrives without the modules' private options, their proxies and the secrets the server needs. DMS_SESSION_SECRET must stay the same across builds, starts and restarts. See Configuration.
Build
ajs dms build
The build takes a few minutes: four minutes for the twelve modules of the DMS demo template. It ends with where it wrote the build and the command to run next. See ajs dms build.
Start
ajs dms start -p 3001
The server binds to 0.0.0.0 and sets Secure cookies. Run it under a process manager that restarts it, and stop it with SIGINT or SIGTERM. See ajs dms start.
Put it behind HTTPS
Terminate TLS in a reverse proxy in front of the server, and tell the server how many proxies it can trust with DMS_TRUSTED_PROXY_HOPS, so that it reads the client's address and country from them. See Security.
start serves HTTP/1.1. In production, use a reverse proxy that speaks HTTP/2 or HTTP/3 to browsers, such as nginx, Caddy or a CDN. Pages load multiple JavaScript chunks: multiplexing those requests avoids the browser's per-origin HTTP/1.1 connection limit.
Where the build lives
build and start find the build through the backend URL: the workspace under ~/.antelopejs/dms-frontend/<sha256>/ keyed on it. Run both as the same user, with the same home directory and the same DMS_API_BASE_URL. In a container image, run build while building the image and start as its command, so the workspace and its node_modules ship with the image.
The served output is the workspace's dist/ directory plus server.mjs, server/ and the workspace's node_modules. Deploy the whole dist/server directory: it holds the e-mail renderer and the translation catalogs it loads at render time.
Rebuilding
The build is a snapshot of the frontend modules the backend served when it ran. Rebuild after installing, removing or upgrading a backend module that ships a frontend module, and after upgrading @antelopejs/dms-frontend. When the backend is down, ajs dms build --offline rebuilds from the last manifest and archive the workspace cached, with a warning that the output may not match the current backend.
Once ready, start posts the frontend modules the build serves and the renderer version to POST /dms/frontend/ready, which the DMS Modules page reads. This report is best effort: an older backend without the endpoint can return 404. In development, the renderer reports again after each manifest read that finds the module set unchanged, so a restarted backend receives it within the three-second polling interval.
Caching in front of the server
Page documents and Inertia answers are private, no-cache by default, since they carry the visitor's session. A module can make an anonymous page cacheable by a shared cache or a CDN with a route rule. Built assets are pre-compressed with Brotli and gzip, and the server sends the compressed copy to a browser that accepts it, with Vary: Accept-Encoding.
Built files carry their MIME type. Content-hashed files under /assets/ use public, max-age=31536000, immutable. Other files, including a layer's public/ files, use no-cache, an ETag and a Last-Modified: browsers revalidate them and receive 304 without a body while they remain unchanged. Import frequently used files such as fonts through CSS or code so Vite gives them content-hashed names under /assets/.
Scaling out
Session refresh is single-flight within one frontend process: when a token expires, one request renews it and the others wait for it. A horizontally scaled deployment must use sticky routing, so that a browser always reaches the same process. The frontend server provides no distributed refresh guarantee.