Skip to content
Saleheen.
All writing
Productivity & Automation

How I automated multi-server startup in my monorepo

Three services, three package managers, three terminals every morning. Here is the ~100-line PowerShell script that replaced all of it with one command.

Reading time
5 min read
Topic
Productivity & Automation

Picture this: you're starting your development day, coffee in hand, ready to build. But before you can write a single line of code, you need to:

  1. Open Terminal #1 → cd backend → npm run dev
  2. Open Terminal #2 → cd frontend → pnpm run dev
  3. Open Terminal #3 → cd mobile-app → bunx expo run:android
  4. Switch between terminals to check if they're running
  5. Forget which terminal is which

Sound familiar? If you work with monorepos or multi-service applications, you've been there. I was losing two to three minutes every single morning on this repetitive task. Today I'm sharing how I automated the entire process with a simple PowerShell script and a package.json integration that saves me time and mental energy every day.

The problem: terminal chaos in monorepos

Why this matters

Modern full-stack development often involves multiple services running simultaneously:

  • Backend API — Node.js/Express, NestJS, and friends
  • Frontend application — Next.js, React, Vue
  • Mobile app — React Native, Expo
  • Sometimes more: microservices, worker processes, background jobs

Each service requires its own terminal, its own working directory, a specific startup command, and a manual check that it's actually running.

The real cost

Beyond the obvious time waste, the hidden costs are worse:

  1. Context switching — jumping between terminals breaks your flow.
  2. Mental overhead — remembering which terminal does what.
  3. Error-prone — easy to run the wrong command or forget a service.
  4. Scalability — it gets exponentially worse with every service you add.

My specific use case

In my courier management system monorepo I needed to run three things:

  • Backend API — Node.js with Prisma ORM
  • SaaS dashboard — Next.js with pnpm
  • Mobile app — React Native with Expo

Three different package managers, three different commands, three different directories.

The solution: PowerShell + Windows Terminal integration

I wrote a PowerShell script that leans on Windows Terminal's tabbed interface to launch every server in organised, colour-coded tabs from a single command.

Key features

  • One command launch — npm run start brings everything up.
  • Colour-coded tabs — each service gets its own colour for instant identification.
  • Selective startup — skip specific services with flags.
  • IDE integration — clickable straight from the VS Code / WebStorm npm scripts panel.
  • Zero configuration — works out of the box once set up.

Step-by-step implementation

Step 1 — create the PowerShell script

Create a file called start-servers.ps1 in your monorepo root:

powershell
param(
    [switch]$SkipApp,
    [switch]$SkipBackend,
    [switch]$SkipSaas,
    [switch]$Help
)

if ($Help) {
    Write-Host "Multi-Server Launcher" -ForegroundColor Green
    Write-Host "Usage:" -ForegroundColor Yellow
    Write-Host "  .\start-servers.ps1                # Start all servers"
    Write-Host "  .\start-servers.ps1 -SkipApp       # Skip mobile app"
    Write-Host "  .\start-servers.ps1 -SkipBackend   # Skip backend"
    Write-Host "  .\start-servers.ps1 -SkipSaas      # Skip SaaS/frontend"
    exit
}

$rootPath = Get-Location
$cmd = "wt --size 170,50"

# Backend
if (-not $SkipBackend) {
    $backendPath = Join-Path $rootPath "backend_folder"
    if (Test-Path $backendPath) {
        $cmd += " --title ""Backend API"" --tabColor ""#68D391"" -d .\backend_folder pwsh -NoExit -Command ""npm run dev"""
    }
}

# Frontend / SaaS
if (-not $SkipSaas) {
    $frontendPath = Join-Path $rootPath "frontend_folder"
    if (Test-Path $frontendPath) {
        if ($cmd -ne "wt --size 170,50") { $cmd += " ``;" }
        $cmd += " sp --horizontal --title ""SaaS Dashboard"" --tabColor ""#9F7AEA"" -d .\frontend_folder pwsh -NoExit -Command ""pnpm dev"""
    }
}

# Mobile app
if (-not $SkipApp) {
    $appPath = Join-Path $rootPath "app_folder"
    if (Test-Path $appPath) {
        if ($cmd -ne "wt --size 170,50") { $cmd += " ``;" }
        $cmd += " sp --horizontal --title ""Mobile App"" --tabColor ""#61DAFB"" -d .\app_folder pwsh -NoExit -Command ""bunx expo run:android"""
    }
}

if ($cmd -eq "wt --size 170,50") {
    Write-Host "No servers selected to start!" -ForegroundColor Red
    exit 1
}

Write-Host "Starting servers..." -ForegroundColor Green
Invoke-Expression $cmd

Step 2 — add npm scripts to package.json

Add these scripts to your root package.json:

json
{
  "scripts": {
    "start": "powershell -ExecutionPolicy Bypass -File ./start-servers.ps1",
    "start:server-saas": "powershell -ExecutionPolicy Bypass -File ./start-servers.ps1 -SkipApp",
    "start:backend": "powershell -ExecutionPolicy Bypass -File ./start-servers.ps1 -SkipApp -SkipSaas",
    "start:frontend": "powershell -ExecutionPolicy Bypass -File ./start-servers.ps1 -SkipApp -SkipBackend",
    "start:app": "powershell -ExecutionPolicy Bypass -File ./start-servers.ps1 -SkipBackend -SkipSaas"
  }
}

Step 3 — customise for your project

  1. Update folder names — replace backend_folder, frontend_folder and app_folder with your real directories.
  2. Update commands — swap npm run dev, pnpm dev and bunx expo run:android for your actual startup commands.
  3. Update colours — change the hex values (#68D391, #9F7AEA, #61DAFB) to taste.
  4. Update titles — rename the tab titles to match your services.

Understanding the code

Windows Terminal command structure

wt --size 170,50 starts Windows Terminal with a specific window size. wt is Windows Terminal's own CLI.

Adding tabs

powershell
--title "Backend API" --tabColor "#68D391" -d .\backend_folder pwsh -NoExit -Command "npm run dev"
  • --title — the tab title shown in the terminal.
  • --tabColor — hex colour for the tab, which is what makes the tabs scannable at a glance.
  • -d — the working directory for that tab.
  • pwsh -NoExit — PowerShell that stays open after the command finishes.
  • -Command — the actual command to run.

Splitting panes

The escaped semicolon separates wt commands, and sp --horizontal creates a horizontal split pane — so multiple tabs share one window instead of stacking up as separate windows.

Conditional logic

if (-not $SkipBackend) { … } is the PowerShell idiom for “the flag was not passed”. When it wasn't, the backend tab is added to the startup sequence.

Benefits and impact

Time savings

  • Before — two to three minutes every morning starting servers by hand.
  • After — one click or command, five to ten seconds.
  • Annual saving — roughly ten to fifteen hours a year if you start dev work daily.

Mental clarity

  • Organised — colour-coded tabs make it obvious which service is which.
  • Consistent — the same setup every time, so there are no mistakes to make.
  • Lower cognitive load — one less thing to hold in your head.

Team productivity

Share the setup with your team and everyone gets:

  • New developers contributing sooner.
  • A consistent development environment.
  • Fewer “it works on my machine” conversations.

Advanced tips and variations

1. Different package managers

powershell
npm run dev     # npm
pnpm dev        # pnpm
yarn dev        # yarn
bun run dev     # bun

2. Custom environment variables

powershell
$cmd += " pwsh -NoExit -Command ""$env:NODE_ENV='development'; npm run dev"""

3. Error handling

powershell
try {
    Invoke-Expression $cmd
    Write-Host "All servers started successfully!" -ForegroundColor Green
} catch {
    Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
}

4. Health checks

After starting, verify the servers actually came up:

powershell
Start-Sleep -Seconds 5
Invoke-WebRequest -Uri "http://localhost:3000" -UseBasicParsing

5. macOS / Linux alternative

The same idea ports to Unix with gnome-terminal or osascript:

bash
#!/bin/bash
gnome-terminal --tab --title="Backend" -- bash -c "cd backend && npm run dev; exec bash"

Common issues and solutions

“wt command not found”

Install Windows Terminal from the Microsoft Store, or make sure it's on your PATH.

Execution policy error

The -ExecutionPolicy Bypass flag in the npm script handles it. If it persists:

powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Script runs but servers don't start

  • Check the folder paths are correct.
  • Check the commands work when run manually.
  • Check the package managers are installed (npm, pnpm, bun).

Tabs close immediately

Make sure -NoExit is in the pwsh command so the tab stays open.

Real-world use cases

  • Full-stack development — API and web app running side by side.
  • Microservices — launch several services at once for local integration testing.
  • Mobile plus backend — a mobile app and the API it talks to.
  • Team onboarding — new hires start contributing on day one.

Conclusion: automate the boring stuff

This small automation has become one of my most valuable developer tools. It isn't really about saving a few minutes — it's about removing friction from your daily workflow, eliminating decision fatigue, creating consistency, and spending your energy on building rather than on setup.

Key takeaways

  1. Automation compounds — small savings add up over months and years.
  2. Simple solutions win — this is about a hundred lines of code with an outsized impact.
  3. The tools exist — Windows Terminal has powerful CLI features; use them.
  4. Share knowledge — if it helps you, it will help your team.

What repetitive tasks have you automated? I'd genuinely like to hear the workflows other developers have sanded down — that's usually where the best ideas come from.

Tags
PowerShellDevToolsMonorepoDeveloper ExperienceAutomation