How to Deploy a Monorepo with Temps: Frontend + Backend in One Push
How to Deploy a Monorepo with Temps: Frontend + Backend in One Push
February 10, 2026 (2w ago)
Written by Temps Team
Last updated February 10, 2026 (2w ago)
Monorepos are everywhere. You have a Next.js frontend, an Express API, maybe a shared types package, and a background worker — all in one repository. The problem? Most deployment platforms want one repo, one app. Deploying a monorepo means fighting your tooling instead of shipping features.
Temps handles monorepos cleanly. This tutorial walks through deploying a real monorepo with a frontend and backend, each as separate Temps projects pointing to different directories in the same repo, all from a single git push.
Why Monorepos Are Hard to Deploy
The Typical Monorepo Structure
my-monorepo/
apps/
web/ # Next.js frontend
package.json
next.config.ts
api/ # Express/Fastify backend
package.json
Dockerfile
packages/
shared/ # Shared types, utils
package.json
ui/ # Shared UI components
package.json
package.json # Root with workspaces
turbo.json # Turborepo config (optional)
What Goes Wrong on Other Platforms
| Platform | Problem |
|---|---|
| Vercel | Deploys one app per project. Separate projects for frontend and API means separate billing, separate env vars, separate deployments |
| Netlify | No native backend support. API needs a separate service entirely |
| Railway | Supports monorepos but charges per service with resource-based pricing |
| Docker Compose | Works but you lose preview deployments, SSL, and monitoring |
The common thread: you end up managing multiple disconnected projects, syncing environment variables across them, and hoping deployments happen in the right order.
How Temps Handles Monorepos
Temps lets you create separate projects that each point to a specific directory within the same Git repository. Each project has its own build context, environment variables, and deployment lifecycle — but they share the same repo and can share database services.
The Key Concept: One Repo, Multiple Projects
Instead of cramming everything into one deployment, Temps treats each app in your monorepo as its own project with a scoped build directory:
Git Repository: my-monorepo
Temps Project: my-saas-web -> directory: apps/web (Next.js, preset: nextjs)
Temps Project: my-saas-api -> directory: apps/api (Node.js, preset: nodejs)
Each project gets its own deployments, its own environment variables, and its own monitoring. But they can share database services and deploy from the same repository.
Tutorial: Deploying a Next.js + Express Monorepo
Let's deploy a real monorepo with a Next.js frontend and an Express API backend.
Prerequisites
- A monorepo with a
webandapiapp (we'll use a typical Turborepo structure) - Git repository on GitHub
- Temps account
Step 1: Authenticate
bunx @temps-sdk/cli login
Step 2: Create Projects for Each App
Create a separate Temps project for each app in your monorepo. Use the --directory flag to scope each project to its subdirectory:
# Create the frontend project
bunx @temps-sdk/cli projects create -n "My SaaS Web" -d "Next.js frontend"
bunx @temps-sdk/cli projects git -p my-saas-web \
--owner myorg --repo my-monorepo --branch main \
--directory apps/web --preset nextjs -y
# Create the API project
bunx @temps-sdk/cli projects create -n "My SaaS API" -d "Express API backend"
bunx @temps-sdk/cli projects git -p my-saas-api \
--owner myorg --repo my-monorepo --branch main \
--directory apps/api --preset nodejs -y
Temps knows to only build the files within each project's directory context.
Step 3: Set Up Environment Variables
Each project has its own environment variables, managed through the environments vars commands:
# List environments to get environment IDs
bunx @temps-sdk/cli environments list -p my-saas-web
bunx @temps-sdk/cli environments list -p my-saas-api
# Frontend variables
bunx @temps-sdk/cli environments vars set -e production \
NEXT_PUBLIC_API_URL "https://api.myapp.com"
# API variables
bunx @temps-sdk/cli environments vars set -e production \
JWT_SECRET "your-secret"
bunx @temps-sdk/cli environments vars set -e production \
STRIPE_SECRET_KEY "sk_live_..."
You can also import from an existing .env file:
bunx @temps-sdk/cli environments vars import -e production -f .env.production
Step 4: Set Up Shared Database
Both your frontend and API likely share a database. Create a database service and link it to both projects:
# Create a PostgreSQL service
bunx @temps-sdk/cli services create -t postgres -n my-saas-db -y
# Link it to both projects (injects POSTGRES_URL automatically)
bunx @temps-sdk/cli services link --id 1 --project-id 1
bunx @temps-sdk/cli services link --id 1 --project-id 2
# Verify the injected environment variables
bunx @temps-sdk/cli services env --id 1 --project-id 1
bunx @temps-sdk/cli services env --id 1 --project-id 2
Both the frontend (for Server Components) and the API connect to the same database. No connection string duplication.
Step 5: Configure Service Communication
Your Next.js frontend needs to talk to the API. Set the internal URL as an environment variable:
// In your Next.js app (apps/web)
const API_URL = process.env.NEXT_PUBLIC_API_URL;
// Server Component fetching from the API
async function getProducts() {
const res = await fetch(`${API_URL}/api/products`, {
next: { revalidate: 60 },
});
return res.json();
}
// In your Express API (apps/api)
import express from "express";
const app = express();
app.get("/api/products", async (req, res) => {
const products = await db.query("SELECT * FROM products");
res.json(products);
});
app.listen(process.env.PORT || 8080);
Step 6: Deploy
Push to your main branch — both projects detect the change and deploy:
git push
Or deploy a specific project manually:
bunx @temps-sdk/cli deploy my-saas-web -b main -e production -y
bunx @temps-sdk/cli deploy my-saas-api -b main -e production -y
Handling Shared Packages
The trickiest part of monorepo deployment is shared packages. If apps/web imports from packages/shared, the build needs access to that package.
With Turborepo
If you're using Turborepo, configure your build commands to respect the pipeline:
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Temps runs the build within the directory context you specified. The monorepo workspace configuration ensures dependencies like packages/shared are resolved correctly.
With pnpm Workspaces
pnpm workspaces work natively:
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
With npm/yarn Workspaces
Same principle — the workspace configuration ensures shared packages are available:
{
"workspaces": ["apps/*", "packages/*"]
}
Preview Deployments for Monorepos
When you open a pull request, each Temps project detects changes in its directory and deploys a preview automatically:
PR #42 opened:
my-saas-web: Preview deployed → pr-42.my-saas-web.temps.dev
my-saas-api: Preview deployed → pr-42.my-saas-api.temps.dev
If only apps/web/ files changed, only the web project rebuilds. The API project stays on its current deployment, keeping preview deployments fast.
Configure Frontend Preview to Use API Preview
Set the API URL for preview environments to point to the API's preview:
# In the web project's preview environment
bunx @temps-sdk/cli environments vars set -e preview \
NEXT_PUBLIC_API_URL "https://pr-42.my-saas-api.temps.dev"
Custom Domains
Set up domains for each project. First, add A records pointing to your server's IP:
| Type | Name | Value |
|---|---|---|
| A | myapp.com | YOUR_SERVER_IP |
| A | api.myapp.com | YOUR_SERVER_IP |
Then add the domains via CLI:
# Frontend — uses HTTP challenge (default)
bunx @temps-sdk/cli domains add -d myapp.com
# API subdomain — uses HTTP challenge (default)
bunx @temps-sdk/cli domains add -d api.myapp.com
By default, Temps uses http-01 challenge to issue SSL certificates via Let's Encrypt. If you need a wildcard certificate (e.g. *.myapp.com), you must use DNS challenge:
# Wildcard domain — DNS challenge required
bunx @temps-sdk/cli domains add -d "*.myapp.com" --challenge=dns-01
With --challenge=dns-01, the CLI will display a TXT record to add to your DNS. Once propagated, Temps completes the ACME validation and issues the certificate.
Monitoring Both Projects
Each project has its own monitoring dashboard. Use the CLI to check logs and deployment status:
# View logs for each project
bunx @temps-sdk/cli runtime-logs -p my-saas-web -f
bunx @temps-sdk/cli runtime-logs -p my-saas-api -f
# Check deployment status
bunx @temps-sdk/cli deployments list -p my-saas-web
bunx @temps-sdk/cli deployments list -p my-saas-api
# Roll back the API if something goes wrong
bunx @temps-sdk/cli deployments rollback -p my-saas-api -e production
Each project has its own:
- CPU and memory usage
- Request count and latency
- Error rate and error tracking
- Deployment history and rollback
Real-World Monorepo Patterns
Pattern 1: Next.js + tRPC
apps/
web/ # Next.js with tRPC client
api/ # tRPC server (standalone or in Next.js)
packages/
trpc/ # Shared router definitions
Deploy as two Temps projects with a shared database service. Type safety from database to UI.
Pattern 2: Frontend + Background Worker
apps/
web/ # Next.js frontend
worker/ # Queue processor (BullMQ)
packages/
jobs/ # Shared job definitions
The worker processes background jobs while the frontend handles user requests. Both deploy from the same repo, each on their own schedule.
Pattern 3: Multi-Tenant SaaS
apps/
app/ # Main application (app.myproduct.com)
marketing/ # Marketing site (myproduct.com)
docs/ # Documentation (docs.myproduct.com)
api/ # Shared API
packages/
ui/ # Shared design system
auth/ # Shared auth logic
Four Temps projects, one repo, each with its own domain and deployment lifecycle.
Migration from Multi-Repo to Monorepo
If you're currently deploying separate repositories for your frontend and backend, consolidating into a monorepo with Temps is straightforward:
Step 1: Restructure
# Move existing apps into a monorepo structure
mkdir -p apps
mv ../frontend apps/web
mv ../backend apps/api
Step 2: Set Up Workspaces
{
"private": true,
"workspaces": ["apps/*", "packages/*"]
}
Step 3: Configure Temps Projects
# Update git settings to point to the new directory
bunx @temps-sdk/cli projects git -p my-web --directory apps/web -y
bunx @temps-sdk/cli projects git -p my-api --directory apps/api -y
Step 4: Deploy
git push
Both projects deploy automatically when their directories change. No more "the frontend deployed but the API didn't" coordination issues.
Quick Reference
# Authenticate
bunx @temps-sdk/cli login
# Create projects for each app
bunx @temps-sdk/cli projects create -n "Web App" -d "Frontend"
bunx @temps-sdk/cli projects git -p web-app \
--owner myorg --repo my-monorepo --branch main \
--directory apps/web --preset nextjs -y
# Set environment variables
bunx @temps-sdk/cli environments vars set -e production KEY "value"
# Import env vars from file
bunx @temps-sdk/cli environments vars import -e production -f .env.production
# Create and link shared database
bunx @temps-sdk/cli services create -t postgres -n shared-db -y
bunx @temps-sdk/cli services link --id 1 --project-id 1
# Deploy (auto on git push, or manual)
bunx @temps-sdk/cli deploy web-app -b main -e production -y
# View logs
bunx @temps-sdk/cli runtime-logs -p web-app -f
# Rollback if needed
bunx @temps-sdk/cli deployments rollback -p web-app -e production
Ready to simplify your monorepo deployment? Get started at temps.sh — your entire stack, deployed from one repo.
Using Turborepo, Nx, or another monorepo tool? The directory-scoped project feature works with any monorepo structure. Check our documentation for more details.