Deploy Angular on Temps
Temps doesn't detect Angular from angular.json, so you pick the preset. For a static SPA, choose the Vite preset and point build.output_dir at dist/<project>/browser: Temps runs ng build and serves the files from its proxy with SPA fallback and HTTPS. For server-side rendering with @angular/ssr, choose the Autopack (Node.js) preset and start server.mjs from a Procfile.
For an overview of self-hosting Angular, including costs, see Deploy Angular to your own server. Every framework and the preset it needs is listed under Supported platforms.
Quickstart
An angular.json is not a detection signal. Deploying without a preset stops with "Could not auto-detect project type from files", so pick one first:
| Your app | Preset | Command |
|---|---|---|
| Static SPA (no SSR) | Vite (vite) | up --preset vite |
@angular/ssr server | Autopack (Node.js) (nixpacks-node) | up --preset nixpacks-node |
You can also set the preset under Project → Settings → Build & deploy → Build → Framework.
# Static SPA npx @temps-sdk/cli up --preset vite
Static SPA
The Vite preset isn't specific to Vite: it installs dependencies with the package manager your lockfile points to, runs your build script and serves one output directory as static files. In an Angular project the build script is ng build, which uses the production configuration by default.
Angular 17 and later write the browser bundle to dist/<project-name>/browser/, not dist/, so tell Temps where it is in a .temps.yaml at the repository root:
.temps.yaml
build:
output_dir: dist/my-app/browser
Use the folder name from your own dist/ directory. Angular 16 and earlier write straight to dist/<project-name>/.
| Feature | How Temps handles it |
|---|---|
| Build | Your build script (ng build) |
| Output | Serves the build.output_dir from .temps.yaml |
| Runtime | No container; the Temps proxy serves the files |
| SPA routing | 404 → index.html fallback |
| Cache | Hashed assets get long-lived cache headers |
| HTTPS | Let's Encrypt, auto-renewed |
| Service workers | ngsw-worker.js and ngsw.json are served as built |
Angular SSR (@angular/ssr)
This guide describes how the nightly release channel builds your app, using the Autopack builder. Beta (v0.1.0-beta.56) also builds with Autopack, but some details below may differ. Earlier releases and the stable channel build with Nixpacks and behave differently. To run nightly, see Release channels.
Apps created with ng new --ssr or ng add @angular/ssr build a Node.js server next to the browser bundle. Choose the Autopack (Node.js) preset (nixpacks-node) so Temps runs that server in a container:
npx @temps-sdk/cli up --preset nixpacks-node
Temps runs your build script, then starts the app. The start script in a new Angular project is ng serve, the development server, so don't rely on it. Add a Procfile to the repository root that starts the server bundle; a web: line takes priority over the start script:
Procfile
web: node dist/my-app/server/server.mjs
Use the path from your own dist/ folder. The generated server.ts reads the PORT variable that Temps sets and falls back to 4000, and serves the browser bundle as well. Angular 16 and earlier, which used Angular Universal, wrote dist/<project-name>/server/main.js instead. The command is the same one your package.json runs as serve:ssr:<project-name>.
Don't set build.output_dir for an SSR app: it makes Temps serve the browser files statically instead of running your server.
Angular's SSR server only answers requests for host names it allows, and Temps' health check reaches the container at 127.0.0.1. Set NG_ALLOWED_HOSTS to 127.0.0.1 plus every domain the app is served on, separated by commas:
npx @temps-sdk/cli environments vars set NG_ALLOWED_HOSTS "127.0.0.1,app.example.com,*.example.com" -e production
Without 127.0.0.1 the health check gets a 400 Bad Request and the deployment times out. Without your domains, visitors get the same error. Add each custom domain to the list when you attach it, and pass --preview to include preview environments.
The Node.js version comes from .nvmrc, .node-version or engines.node in package.json, and defaults to 24.
Platform behavior
These rules apply to every server app deployed on Temps.
The one requirement: your app must listen on the port in the PORT environment variable and bind to 0.0.0.0 — not localhost or 127.0.0.1. Temps runs your app in a container and routes traffic from the host, so an app bound to localhost only accepts connections from inside the container and will fail its health check.
Health checks
After your container starts, Temps sends HTTP GET requests to verify it is healthy before routing traffic to it.
- Path:
/(the root of your application) by default - Success: 2 consecutive responses with a 2xx, 3xx, 404 or 405 status code
- Timeout: 300 seconds (5 minutes) for the app to become healthy, fixed in v0.1.0-beta.56. If the app answers with error responses (other 4xx, or 5xx) for 60 seconds in a row, the deployment fails sooner. From the next release the timeout is configurable from 30 to 3600 seconds under Project → Settings → Build & deploy
- Retry interval: 2 seconds between successful checks, 5 seconds after an error response or a failed connection
Connection errors and other 4xx or 5xx responses are retried until the timeout; if the app hasn't passed by then, the deployment fails. The check runs once, at deploy time. To check a different path, add a .temps.yaml to your repository root:
health:
path: /healthOnly path is used. Other health fields (status, interval, timeout, retries) are accepted but currently have no effect.
/health endpoint that returns a simple 200. This avoids issues where / requires authentication or returns a redirect.Environment-specific builds
Angular's environment.ts files work as-is, but their values are compiled into the bundle, so they change only with a new build. For values that differ per environment without a rebuild, load them at startup from a config endpoint or a window.__env script.
Environment variables
npx @temps-sdk/cli environments vars set API_URL "https://api.example.com" -e production
A static build only sees values compiled in at build time. An SSR server reads variables at runtime through process.env.
SPA routing
This applies to static SPAs served by the proxy. With SSR, the Angular server handles routing.
Single-page applications use client-side routing — paths like /about or /dashboard/settings do not correspond to files on disk. The Temps proxy handles this automatically.
When a request arrives for a path that:
- Has no file extension (e.g.
/about, not/about.css) - Does not match any file on disk
…the proxy serves index.html instead, and your client-side router (React Router, Vue Router, etc.) takes over. This works out of the box — you do not need _redirects, vercel.json, or any rewrite rules.
Caching
The Temps proxy sends ETags with static files, so a browser that already has an unchanged file gets 304 Not Modified instead of downloading it again. The proxy doesn't compress static files.
Cache-Control headers are set based on the file path:
| Pattern | Cache-Control | Use case |
|---|---|---|
/assets/*, /static/*, /_next/static/*, .chunk.*, .hash.* | public, max-age=31536000, immutable | Hashed assets that never change |
| Everything else | public, max-age=0, must-revalidate | HTML files and non-hashed resources |
Your hashed JS and CSS bundles are cached for a year, while index.html is always revalidated so new deployments are picked up immediately.
Deploying another single-page app? The React and Vue guides use the same Vite preset.