Deploy one folder of a monorepo

Your repo holds several projects and only one belongs on this server. Sync paths deploy just the folder you want; exclude paths keep named paths out. Both cap at 20, and the trick is pointing your Main file into the synced folder.

Plenty of repositories hold more than one project — a bot and its dashboard, a web app beside its shared library, three services in one place. When only one of them belongs on this server, you don't want the whole repo landing in /home/container. The Git page's sync paths and exclude paths solve exactly this. This guide is the deep dive.

At a glance
You need a server with a repository linked on its Git page (see Deploy from Git)
Plan any
Time fifteen minutes

The two path filters

On the Git settings you'll find two lists, each holding up to 20 paths:

Filter What it does Applied
Sync paths Limits the deploy to only these directories — everything else in the repo is skipped First
Exclude paths Keeps named paths from being copied at all After the sync filter

They work together and in that order: sync paths pick the folders that come in, then exclude paths subtract anything you still don't want from within them. Leave sync paths empty and the whole repo deploys (the default); add one and suddenly only that folder lands.

Paths are plain and relative to the repo root — letters, digits, dashes, dots, and slashes, like apps/bot or services/api. No wildcards or globs; you name real directories.

The catch that trips everyone up: the Main file

Here's the part people miss. Say your repo looks like this:

my-monorepo/
├── apps/
│   ├── bot/
│   │   ├── index.js
│   │   └── package.json
│   └── web/
└── packages/

You set sync paths to apps/bot, deploy, and… the server won't start. Why? Because sync paths keep the folder structure. apps/bot/index.js lands at /home/container/apps/bot/index.js, but the Node application looks for its entry file at the root/home/container/index.js, which doesn't exist.

The fix is one setting: point the Main file variable (Settings page → Environment) at the real location, apps/bot/index.js. Python's the same idea with the App py file variable, PHP with its PHP File. Sync paths put your folder on the server; the application's entry-file variable tells the runtime where to find it inside that folder.

⚠️ Heads up: Node's Main file field holds at most 16 characters, so a deep path like services/worker/index.js won't fit. If your entry path is too long, sync that folder as the only one and restructure so the entry file lands nearer the root, or keep the subfolder shallow.

Worked example 1: a bot in a subfolder

Your repo has apps/bot (the Discord bot) and apps/web (an unrelated site). This server should run only the bot.

  1. Sync paths: add apps/bot. Now a deploy copies only that folder.
  2. Main file: set it to apps/bot/index.js.
  3. Deploy, then Start. package.json inside apps/bot triggers the automatic npm install, and the bot boots.

Your apps/web folder never touches the server, so there's nothing to trip over and nothing wasted.

Worked example 2: a service plus a shared library

Now a trickier shape — your service imports a shared package that lives elsewhere in the repo:

repo/
├── services/api/      (this server runs this)
├── packages/shared/   (api imports from here)
└── services/worker/   (belongs on a different server)

The service needs services/api and packages/shared, but not services/worker:

  1. Sync paths: add both services/api and packages/shared. Sync paths are a list — you can name several folders, and only those come in.
  2. Exclude paths: you don't even need one here, because services/worker was never synced. Exclude paths earn their keep when a folder you did sync contains something you want to leave out — say you sync services/api but want to skip services/api/fixtures. Add services/api/fixtures to excludes and it's dropped after the sync.
  3. Point the Main file at services/api/index.js and make sure your imports resolve to the synced packages/shared path.

Everything else about deploys still applies

Sync and exclude paths only decide which files come in. The rest of the Git model is unchanged:

  • Copy, don't replace. A deploy overwrites files by name and never deletes server files that aren't in the source — so your server-side .env and data survive, exactly as in a normal deploy. Exclude paths are a second layer of protection: name a path there and even a same-named repo file won't overwrite it.
  • Build steps run after the copy — see Build steps on deploy if your synced folder needs compiling.
  • Auto-deploy works with path filters too; a push redeploys just the synced folders.

Troubleshooting

  • Deploy succeeds but the server won't start — almost always the Main file. Your entry file is inside the synced subfolder; point the Main file (or App py file / PHP File) variable at its real path.
  • Files from a folder I didn't want still showed up — that folder is in (or implied by) your sync paths, or sync paths are empty (which deploys everything). Tighten the sync list, or add the unwanted path to excludes.
  • My entry path is longer than 16 characters (Node) — the Main file field won't hold it. Sync a shallower folder or restructure so the entry file sits nearer the root.
  • A deploy failed on the path filter — paths are plain relative directories (letters, digits, dashes, dots, slashes); no wildcards. Re-check for a typo or a leading slash.

Next steps

Was this guide helpful?