— Article — № 032

032 —Business

Self-hosting client work: a small studio's case for owning the dock

A three-person studio runs eleven legacy sites. The argument for self-hosting the tooling around that work, instead of renting it, gets stronger every quarter.

Flat-lay of studio intake binder with eleven manila tabs, ledger, brass plate, wax seal, invoice on bone linen.
Hero · staged still№ 032

It's a Tuesday in May, the kind where the office is quiet because two of the three of you are on-site. The remaining person, you, is staring at an invoice from a CI vendor that has quietly tripled since January. The line item is "compute minutes". The work it actually did this month was running composer install and rsync against eleven legacy WordPress and Magento sites you've inherited from clients who didn't write the original code and don't want to.

You do the math on a Post-it. Eleven sites, four deploys a week on the busy ones, two on the quiet ones, call it 110 deploys a month. The bill comes out to roughly two euros per deploy, for a job that takes 90 seconds of CPU on the Mac mini sitting under the desk. That Mac mini is on. It's been on for three years. It has spare cycles.

This is the moment a lot of small studios start asking the older question: which links in our chain do we actually need to rent, and which ones could we own outright? Self-hosting client work doesn't mean going back to dragging zips over FTP. It means treating the dock, the things that sit between you and the legacy site, as infrastructure you control, not infrastructure you lease per minute.

The four rooms of a studio's dock

Before deciding what to host, it helps to name the rooms. A working studio chain has four of them, and they're usually all rented:

  • The source room: where the code lives. GitHub or GitLab, usually.
  • The review room: where a branch becomes a clickable URL for a client. Vercel previews, Netlify, a staging WP-Engine slot.
  • The deploy room: the thing that ships the bytes. CircleCI, GitHub Actions, a bespoke runner.
  • The secrets room: where SFTP credentials, database passwords and API keys live. 1Password, Bitwarden, a .env nobody talks about.

Each room has a monthly cost and a vendor-risk cost. You can rent all four. You can own all four. Most studios that have been around a while end up owning two, renting two, and the choice of which two is the interesting part.

Rent the source. Own the deploy.

The cheapest room to rent is the source room. GitHub's free tier is fine for a three-person shop. Mirroring is one cron job. If GitHub goes down for the morning, you keep working from local clones and push later. Low pain.

The deploy room is where the math flips. CI vendors price by the minute, and a legacy site's deploy is mostly waiting, for composer, for rsync, for the database to finish a migration. You pay for that waiting. A Mac mini, a small NUC, or a €4/month VPS will do the same work and stop charging when it's done.

The crudest possible setup is a shell script and a webhook listener. Here's a sketch of the deploy half, the kind of thing that lives in /usr/local/bin/deploy-site.sh on the in-house box:

#!/usr/bin/env bash
set -euo pipefail

SITE="$1"
BRANCH="${2:-main}"
CONFIG="/etc/studio-deploys/${SITE}.env"

[[ -r "$CONFIG" ]] || { echo "no config for $SITE"; exit 2; }
source "$CONFIG"

WORK="/var/studio/builds/${SITE}"
mkdir -p "$WORK"
cd "$WORK"

if [[ ! -d .git ]]; then
  git clone --quiet "$REPO" .
fi

git fetch --quiet origin
git checkout --quiet "$BRANCH"
git reset --hard "origin/${BRANCH}"

composer install --no-dev --quiet --no-interaction

rsync -az --delete \
  --exclude='.git' --exclude='wp-content/uploads' \
  ./ "${SFTP_USER}@${SFTP_HOST}:${SFTP_PATH}/"

echo "deployed ${SITE}@${BRANCH} at $(date -Iseconds)"

A webhook receiver in front of that, adnanh/webhook is 90 lines of YAML, and you've replaced the deploy room for the cost of electricity. The script is auditable. When it breaks at 23:00, you read it. You don't open a support ticket.

Secrets stay home

The secrets room is the one studios most often get wrong, because the temptation is to leave it in the same place the deploys run. Don't. Keep the secrets in something purpose-built, Bitwarden's self-hosted server, or even a plain age-encrypted file in a private repo, and have the deploy box pull credentials at deploy time, not store them at rest.

A .env file on a box that also accepts webhooks is the single most common way small studios lose client SFTP credentials. The OWASP guidance on secrets management is worth reading once a year even if you think you know it. The short version: the credential should arrive at the moment of use, and leave when the process exits.

The review room is the hard one

Previews are where the rent-vs-own math gets genuinely murky. A Vercel preview URL on every PR is a real feature. Replicating it for a legacy PHP site means a server that can stand up a fresh copy of the site, with a fresh database snapshot, on demand, and tear it down when the branch closes.

For a Next.js marketing site, rent. The pricing is fair and the engineering is not yours to do. For a WordPress site with 14 GB of wp_postmeta and three custom plugins from 2014, own. No SaaS preview product handles that well, and the ones that try charge enterprise rates. A nightly snapshot, a per-branch subdomain, and a one-line wp search-replace step is enough:

wp search-replace 'https://client.example' \
  "https://${BRANCH}.preview.studio.internal" \
  --all-tables --skip-columns=guid

That gives you clickable previews for the same monthly cost as the Mac mini already running the deploys. The interesting question, increasingly, isn't whether to self-host but which slice to self-host first. The slice that's almost always worth owning is the one closest to the client's production server, because that's where the bytes actually have to land.

The honest cost of owning

Self-hosting is not free. It costs attention. Your in-house deploy box needs OS updates, disk monitoring, and a backup of its own configuration. The right way to think about that cost is in hours per month, not in euros.

A rough budget for a three-person studio:

  • 15 minutes/week of routine: checking the deploy log, applying security updates, rotating the offsite backup.
  • 1 hour/quarter of maintenance: upgrading the deploy script, pruning old preview environments, rotating long-lived credentials.
  • 4 hours/year of rebuild rehearsal: spinning up the whole dock on a fresh machine from documentation, to prove the documentation still works.

That last one is the discipline most studios skip, and it's the one that makes self-hosting safe. If you can't rebuild the dock from notes in an afternoon, you don't own it, you're just renting from yourself, badly. The rebuild rehearsal also catches the slow drift where one team member installed something "just for now" eighteen months ago.

Where the line sits in 2026

The line we've watched settle, across our own studio work and the small agencies we talk to, looks roughly like this. Source: rent. Issue tracking: rent. Deploys, secrets, review environments for legacy stacks, and the database tooling that goes with them: own. The reasoning is consistent. Anything priced per minute or per environment, where your usage is bursty and your work is mostly waiting, rewards owning. Anything priced per seat, where the vendor is doing genuine engineering you'd otherwise replicate, rewards renting.

When we built Pier we kept running into the database half of the review room, the bit where staging a real client snapshot for a clickable preview requires a MySQL editor you actually trust and a clean version history when the migration goes sideways. The way we ended up handling it was to put both into the same Mac-native app, so the dock stays on the studio's machine and nothing about the client's database ever transits a third party.

The smallest thing you can do today: open last month's invoices from your CI vendor and your preview-environment vendor, add them up, and divide by the number of deploys you actually shipped. If the number is more than a euro, the rest of this post is worth a second read.

— Questions —

Does self-hosting mean giving up GitHub or GitLab?

No. Source hosting is the cheapest room to rent and the most painful to own. The case is for self-hosting the deploys, secrets and review environments that sit downstream of source.

What hardware is enough for a three-person studio?

A Mac mini, a small Intel NUC, or a €4-€8/month VPS handles dozens of legacy-site deploys a week. The bottleneck is almost always network bandwidth to the client's server, not CPU.

Isn't self-hosting just shifting the maintenance burden to you?

Yes, and that's the trade. Budget around 15 minutes a week of routine plus a yearly rebuild rehearsal. If you can't spare that, keep renting. The math only works if the discipline is real.

How do I keep client SFTP credentials safe on a self-hosted box?

Store them in a separate secrets manager (self-hosted Bitwarden, an age-encrypted file, or similar) and have the deploy process pull them at runtime rather than keeping a .env file on disk.