Skip to content

Multi-Service Monorepo

Pilum is designed for monorepos with multiple services. This guide covers project organization, dependency management, and deployment strategies.

my-project/
├── .pilumignore # Exclude examples, tests, etc.
├── services/
│ ├── api-gateway/
│ │ ├── pilum.yaml
│ │ ├── Dockerfile
│ │ └── main.go
│ ├── user-service/
│ │ ├── pilum.yaml
│ │ ├── Dockerfile
│ │ └── main.go
│ └── worker/
│ ├── pilum.yaml
│ ├── Dockerfile
│ └── main.go
├── shared/ # Shared libraries (no pilum.yaml)
│ ├── auth/
│ └── database/
└── tools/ # CLI tools
└── my-cli/

Service Discovery

Pilum automatically discovers all pilum.yaml files in your project:

Terminal window
$ pilum list
Services found:
api-gateway gcp us-central1
user-service gcp us-central1
worker gcp us-central1
my-cli homebrew

Flat vs Nested Structure

Both structures work. Choose based on your team’s preferences:

Flat (services at root):

my-project/
├── api-gateway/
│ └── pilum.yaml
├── user-service/
│ └── pilum.yaml
└── worker/
└── pilum.yaml

Nested (grouped by domain):

my-project/
├── services/
│ ├── api/
│ │ └── pilum.yaml
│ └── workers/
│ ├── email-worker/
│ │ └── pilum.yaml
│ └── payment-worker/
│ └── pilum.yaml
└── tools/
└── cli/
└── pilum.yaml

Service Dependencies

Use depends_on to control deployment order:

services/database/pilum.yaml
name: database
provider: gcp
# ...
services/api/pilum.yaml
name: api
provider: gcp
depends_on:
- database # Deploys after database
services/worker/pilum.yaml
name: worker
provider: gcp
depends_on:
- api # Deploys after api
- database # Also depends on database

Pilum builds a dependency graph and deploys in topological order:

Terminal window
$ pilum deploy --tag=v1.0.0
Deploying 3 services...
[1/3] database
[2/3] api
[3/3] worker

Circular Dependencies

Pilum detects and rejects circular dependencies:

Terminal window
$ pilum check
Error: circular dependency detected: api -> worker -> api

Selective Deployment

Deploy Specific Services

Terminal window
# Deploy only api and worker
pilum deploy api worker --tag=v1.0.0

Deploy Changed Services Only

Use --only-changed to deploy only services with git changes:

Terminal window
# Deploy services changed since main branch
pilum deploy --only-changed --tag=v1.0.0
# Compare against specific ref
pilum deploy --only-changed --since=v1.0.0 --tag=v1.1.0

This also includes services that depend on changed services:

Terminal window
$ pilum deploy --only-changed --tag=v1.0.0 --debug
DEBUG: Service database has direct changes
DEBUG: Service api included (depends on changed service)
Found 2 service(s) with changes (1 direct, 1 via dependencies)

Parallel Execution

Pilum deploys independent services in parallel:

Terminal window
# Auto-detect optimal worker count
pilum deploy --tag=v1.0.0
# Limit parallelism
pilum deploy --max-workers=2 --tag=v1.0.0
# Sequential (one at a time)
pilum deploy --max-workers=1 --tag=v1.0.0

Services with dependencies are deployed in order, but independent services run concurrently.

Mixed Providers

A single monorepo can have services for different providers:

services/api/pilum.yaml
name: api
provider: gcp
project: my-gcp-project
region: us-central1
services/lambda-handler/pilum.yaml
name: lambda-handler
provider: aws
region: us-east-1
function_name: my-lambda
tools/cli/pilum.yaml
name: my-cli
provider: homebrew
project: my-org

Deploy all services together:

Terminal window
pilum deploy --tag=v1.0.0

Multi-Region Services

A single service can deploy to multiple regions:

services/global-api/pilum.yaml
name: global-api
provider: gcp
project: my-project
regions:
- us-central1
- europe-west1
- asia-east1

Pilum expands this into three deployments:

Terminal window
$ pilum list
Services found:
global-api (us-central1) gcp
global-api (europe-west1) gcp
global-api (asia-east1) gcp

Deploy specific regions:

Terminal window
# Deploy all regions
pilum deploy global-api --tag=v1.0.0
# Deploy specific region
pilum deploy "global-api (us-central1)" --tag=v1.0.0

Excluding Services

Use .pilumignore to exclude directories:

.pilumignore
# Development only
examples
fixtures
test-*
# Archived services
archived/
*-deprecated
# Templates
templates/

See Ignore Files for full syntax.

Naming Conventions

Consistent naming helps with filtering and organization:

PatternExampleUse Case
{domain}-{type}user-api, user-workerDomain-driven design
{type}-{name}api-gateway, worker-emailType-first grouping
{env}-{name}prod-api, staging-apiEnvironment separation

CI/CD Tips

Deploy Changed Services

.github/workflows/deploy.yml
- name: Deploy changed services
run: pilum deploy --only-changed --tag=${{ github.sha }}

Deploy by Label/Path

# Deploy services in changed directories
- name: Get changed services
id: changes
run: |
SERVICES=$(git diff --name-only origin/main | grep pilum.yaml | xargs dirname | sort -u)
echo "services=$SERVICES" >> $GITHUB_OUTPUT
- name: Deploy
run: pilum deploy ${{ steps.changes.outputs.services }} --tag=${{ github.sha }}

Validation on PR

.github/workflows/pr.yml
- name: Validate configs
run: pilum check
- name: Preview deployment
run: pilum dry-run --tag=pr-${{ github.event.pull_request.number }}

Next Steps