Last updated on 21st February 2026

Deploying a monorepo to multiple servers

If your repository contains multiple components, such as a backend API, a frontend application, an admin panel, or static assets, you can use DeployHQ to deploy each component to a different server from a single deployment. This is useful for monorepos, modular monoliths, or any project where different parts of the codebase need to go to different destinations.

How it works

DeployHQ's server groups allow you to deploy to multiple servers in a single deployment. Combined with per-server excluded files, you can control exactly which files are sent to each server. When a deployment runs:

  1. The repository is checked out and any build commands run once
  2. DeployHQ generates a file manifest for the deployment
  3. Each server in the group receives only the files that are not excluded for that server

This means you can have one repository, one build pipeline, and one deployment trigger, but each server only receives the files it needs.

Example: Laravel API with a React frontend

Consider a monorepo with the following structure:

/
├── app/                  # Laravel backend
├── bootstrap/
├── config/
├── database/
├── public/
│   └── build/            # Vite-compiled assets
├── resources/
├── routes/
├── frontend/             # React SPA source
│   ├── src/
│   ├── dist/             # Built frontend (generated by build pipeline)
│   └── package.json
├── composer.json
├── package.json
└── vite.config.js

You want to deploy the Laravel API to a web server via SSH, the compiled React frontend to a CDN or static hosting server, and the Vite-built assets to Cloudflare R2.

Step 1: Create your servers

Add three servers to your project under Servers & Groups:

  • API Server (SSH/SFTP) - Your web server running the Laravel application
  • Frontend Server (SSH/SFTP or S3-compatible) - Where the compiled React SPA is served from
  • Asset Storage (S3-Compatible Storage) - Cloudflare R2 bucket for CSS/JS assets

For S3-compatible storage setup, see Configuring S3-Compatible Storage.

Step 2: Create a server group

Go to Servers & Groups and click New Server Group. Give it a name like "Production" and choose your transfer order:

  • Parallel deploys to all servers at the same time, which is faster
  • Sequential completes one server before starting the next

Then edit each of the three servers and assign them to this group using the Group dropdown.

Step 3: Configure your build pipeline

Set up your build commands to compile all assets in a single pipeline. For example:

Command 1 - Install and build the React frontend: cd frontend && npm ci && npm run build

Command 2 - Install Laravel dependencies and build Vite assets: composer install --no-dev --optimize-autoloader npm ci && npm run build

The build pipeline runs once per deployment, and the generated files are available to all servers in the group.

Step 4: Configure excluded files per server

This is the key step. Go to Settings > Excluded Files and add rules for each server. When adding each rule, uncheck Exclude this file on all current and future servers? and select only the server it applies to.

API Server - Exclude frontend source and build tooling:

Excluded path Applies to
frontend API Server only
frontend/** API Server only
node_modules API Server only
node_modules/** API Server only
package.json API Server only
package-lock.json API Server only
vite.config.js API Server only

Frontend Server - Exclude everything except the built React app:

Excluded path Applies to
** Frontend Server only
!frontend/dist/** Frontend Server only

Asset Storage - Exclude everything except Vite-built assets:

Excluded path Applies to
** Asset Storage only
!public/build/** Asset Storage only

The ** rule excludes all files, and the ! prefix whitelists specific directories back in. This approach requires fewer rules and is easier to maintain. See the Excluded Files documentation for more details on pattern matching and whitelisting.

Step 5: Deploy

When a deployment is triggered (manually or via automatic deployments):

  1. The build pipeline runs both build commands
  2. The API Server receives the Laravel application files (without frontend source)
  3. The Frontend Server receives only the compiled React SPA from frontend/dist/
  4. The Asset Storage receives only the Vite-built CSS/JS from public/build/

All three transfers happen within the same deployment, keeping everything in sync.

SSH commands per server

SSH commands such as cache clearing, queue restarts, or migration commands only run on servers that support shell execution (SSH/SFTP). They are automatically skipped for cloud storage servers like S3 or R2.

You can configure different commands for different servers. For example:

  • API Server: Run php artisan migrate --force after deployment
  • Frontend Server: Run nginx -s reload to clear the cache

When adding an SSH command, uncheck the option to run on all servers and select only the server where it should execute.

Other common patterns

Microservice-style monorepo

A monorepo containing multiple independent services:

Server Receives Excludes
User Service (SSH) services/users/, shared/ Other services
Order Service (SSH) services/orders/, shared/ Other services
Gateway (SSH) gateway/, shared/ All services

WordPress with a static frontend

Server Receives Excludes
Web Server (SSH) WordPress theme, plugins, PHP files src/, build tooling
CDN (S3/R2) Compiled CSS/JS, optimised images PHP files, source files

Documentation site alongside an application

Server Receives Excludes
App Server (SSH) Application code docs/
Docs Server (SSH or S3) Built documentation from docs/dist/ Application code

Mixing server protocols

A server group can contain servers using different protocols. You can combine SSH/SFTP servers, FTP servers, S3 buckets, Cloudflare R2, Shopify, and other supported protocols in the same group. DeployHQ handles the file transfer using the appropriate protocol for each server.

The only restriction is that Shell servers cannot be mixed with other server types in the same group.

Tips

  • Start with whitelisting: For servers that only need a small subset of files, use ** to exclude everything and then whitelist specific paths with !. This is easier to maintain than listing every directory to exclude.
  • Use .deployignore for shared rules: If some exclusions apply to all servers (like node_modules or .git), add them to a .deployignore file in your repository. Use the DeployHQ interface for per-server rules only.
  • Test with a preview deployment: Before deploying to production, use the deployment preview to verify that each server is receiving the correct files.
  • Consider build pipeline per server: If different servers need different build configurations (for example, different Node.js versions), you can set up server-specific build pipelines.