> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slipway.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Monorepos

> Deploy several apps from one repository, and rebuild only what changed.

A repository holding more than one app deploys as several services. Each one tracks its own
state — its own deploys, status, URL and history — so a push starts a separate deployment
per service and a broken build in one leaves the others serving. Two fields control the
build: `rootDir` sets where a service builds from, and `buildFilter` decides whether a push
rebuilds the repository's services at all.

## Setting a root directory

`rootDir` is the directory a service builds from. Paths inside the service, including
`dockerfilePath` and `publish`, are relative to it.

```yaml slipway.yaml theme={"system"}
version: 1

services:
  - name: api
    rootDir: backend
    runtime: docker
    ports:
      - port: 8080
        public: true

  - name: web
    rootDir: frontend
    runtime: node
    buildCommand: npm run build
    ports:
      - port: 3000
        public: true
```

If you omit `rootDir` on a service that builds, it defaults to the repository root.

`dockerContext` narrows the Docker build context below `rootDir`, for the case where the
Dockerfile needs to see less than the whole directory.

## Rebuilding only what changed

By default, a push to the tracked branch redeploys every service in the source that has
auto-deploy on, whichever files changed. A `buildFilter` restricts that to pushes which
touch files you care about.

```yaml slipway.yaml theme={"system"}
version: 1

buildFilter:
  paths:
    - backend/**
  ignoredPaths:
    - backend/docs/**
    - "**/*.md"

services:
  - name: api
    rootDir: backend
    runtime: docker
```

| Field | Type | Notes |
| - | - | - |
| `paths` | list of globs | A changed file must match one of these. An empty list means every file matches. |
| `ignoredPaths` | list of globs | A changed file matching any of these does not count, even if it matched `paths`. |

At least one of the two must be non-empty. A block declaring neither fails validation, and
so does an empty or whitespace-only pattern, or one that is not a valid glob.

### How a push is evaluated

A changed file is relevant when it matches `paths` (or `paths` is empty) **and** matches no
entry in `ignoredPaths`. If a push contains no relevant file, the auto-deploy is skipped.

```mermaid theme={"system"}
flowchart LR
  push{{"Changed file"}}
  inc{{"Matches paths?"}}
  ign{{"Matches ignoredPaths?"}}
  yes["Rebuilds"]
  no["Skipped"]

  push e1@--> inc
  inc e2@-->|"no"| no
  inc e3@-->|"yes"| ign
  ign e4@-->|"yes"| no
  ign e5@-->|"no"| yes

  e1@{animation: slow}
  e2@{animation: slow}
  e3@{animation: slow}
  e4@{animation: slow}
  e5@{animation: slow}

  classDef choice fill:#0D857714,stroke:#0D8577,stroke-width:1.5px
  classDef result fill:#2DD4BF22,stroke:#0D8577,stroke-width:2px
  class push,inc,ign choice
  class yes,no result
```

> [!WARNING]
>
> Build filter paths are always relative to the **repository** root, even when the service
> sets a `rootDir`. A service with `rootDir: backend` still needs `backend/**`, not `**`.

A manual deploy always runs. Build filters only affect what a push triggers, so you are
never locked out of deploying.

## A worked layout

For a repository shaped like this:

```
.
├── backend/
├── frontend/
└── shared/
```

Give each service a `rootDir`, and set the filter so a change to `shared/` rebuilds both:

```yaml slipway.yaml theme={"system"}
version: 1

buildFilter:
  paths:
    - backend/**
    - frontend/**
    - shared/**
  ignoredPaths:
    - "**/*.md"

services:
  - name: api
    rootDir: backend
    runtime: docker
    ports:
      - port: 8080
        public: true

  - name: web
    rootDir: frontend
    runtime: node
    buildCommand: npm run build
    ports:
      - port: 3000
        public: true
```

## Several repositories in one project

A project is not limited to one repository. Add a source per repo and they deploy
together into the same network, reaching each other by service name. See
[Projects](/projects/overview).

Sources added this way default to auto-deploy **off**, so turn it on per source when
you want pushes to redeploy them.

## FAQ

**Does a build filter save money?**

**Yes.** A skipped build runs no build machine, so a push that touches only documentation
costs nothing.

**Can two services share a `rootDir`?**

**Yes.** Two services can build from the same directory with different Dockerfiles or
targets.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.