A common MVP architecture looks like this: the app runs on a $12 VPS, the database lives on the same box because a managed one felt expensive, uploads go to an S3 bucket in a different cloud, and a platform-as-a-service hosts the marketing site. Every piece made sense when you picked it. Together they add up to three dashboards, three bills, and one person on call for all of it.
Then the disk fills up at 2 AM, Postgres stops accepting writes, and you spend six hours learning what a write-ahead log is.
This article explains what cloud computing and managed services actually take off your plate, why the benefit compounds when those services live on one platform, and how to move an existing Docker-based MVP there without rewriting your configuration. The goal is a production-ready stack for your first ten paying users that costs a predictable amount each month and does not need you awake at night.
Cloud computing vs. managed services: where the line sits
"Cloud computing" means renting infrastructure on demand instead of owning hardware. "Managed services" means the provider also operates that infrastructure for you. The two overlap, but they are not the same thing, and the gap between them is where solo founders get hurt.
A useful way to see it is the shared responsibility model: for every layer of your stack, either you or the provider is on the hook.
| Layer | Raw VPS | Managed database | Application platform |
|---|---|---|---|
| Physical hardware, network | Provider | Provider | Provider |
| OS patches, kernel updates | You | Provider | Provider |
| Disk capacity and monitoring | You | Provider (within plan limits) | Provider |
| Runtime, web server, TLS | You | n/a | Provider |
| Backups and restore tooling | You | Provider | n/a |
| Scaling and restarts | You | Provider | Provider |
| Your code, schema, and data | You | You | You |
A VPS is cloud computing, but it is not a managed service. You rent the machine, and everything above the hypervisor is still your job. That is why a "cheap" droplet can be the most expensive part of your stack once you price in your time.
Managed services do not remove responsibility. They move it. You still own your code, your schema, and your data. What you stop owning is the undifferentiated work: patching, disk, backups, certificates, and restarts.
Why one platform beats three good vendors
Picking the best vendor for each layer sounds rational. For a team with a platform engineer, it can be. For a solo founder, each additional vendor adds costs that do not show up on the pricing page.
The seams are where things break
Every boundary between vendors needs something to cross it:
- Credentials: an access key for the bucket, a connection string for the database, a deploy token for CI. Each one is a secret you rotate, store, and occasionally leak.
- Networking: an app in one provider talking to a database in another goes over the public internet. You need TLS, IP allow-lists, and you pay for the latency on every query.
- Egress: moving data between providers is often billed. An app that reads large files from another cloud's bucket can pay for the same bytes twice.
- Observability: when a request is slow, you check three dashboards to find out whether it is the app, the database, or storage.
Decision fatigue is a real cost
Three vendors means three pricing models, three sets of limits, and three places a surprise can come from. A founder who has just received a $180 invoice from a platform with metered RAM knows that the problem was not the amount. It was not knowing it was coming.
One bill is a feature
When app hosting, the database, and storage share a plan, you can answer "what will this cost next month?" before you deploy. For a pre-seed company about to show numbers to investors, that predictability is worth more than shaving a few dollars off any single line item.
The silent disk-space outage, and why managed databases prevent it
It is worth understanding the most common self-hosted failure in detail, because it is exactly what a managed database is designed to prevent.
On a VPS running Docker and Postgres, disk fills from several directions at once:
- Container logs: the default
json-filelog driver keeps logs forever unless you configure rotation. - Old images and build cache: every deploy leaves layers behind.
- Postgres WAL: write-ahead logs pile up if archiving fails or a replication slot is abandoned.
- Backups written to the same disk: the classic mistake of a nightly
pg_dumpto/var/backupson the server it is meant to protect.
If you are still on a VPS, check now:
df -h /
docker system df
sudo du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -5
sudo du -sh /var/lib/postgresql/*/main/pg_wal 2>/dev/nullAnd put a band-aid on log growth until you migrate, in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Restart Docker after changing it (sudo systemctl restart docker). The setting applies to newly created containers, so recreate yours with docker compose up -d --force-recreate.
A managed database removes this entire category: storage is monitored, backups go somewhere other than the database's own disk, and restoring is a platform feature rather than a script you wrote at midnight.
Consolidating your MVP onto one platform
Here is the migration path from "VPS plus assorted vendors" to a unified managed platform. It assumes you already have a working Docker setup, which means you do not need to rewrite anything.
Step 1: inventory what you run
Write down every moving part. Your docker-compose.yml is the best starting point:
docker compose config --services
docker compose config | grep -E "image:|build:|ports:|volumes:"For each service, note whether it is your code (web, API, workers), a stateful dependency (Postgres, Redis), or infrastructure glue (nginx, Certbot, backup cron). Your code moves to the application platform. Stateful dependencies become managed services. Glue disappears, because the platform handles routing, TLS, and backups.
Step 2: separate configuration from the compose file
Anything hard-coded in docker-compose.yml under environment: needs to become an environment variable on the new platform. List them:
docker compose config --format json | jq '.services[].environment'Replace service hostnames such as postgres:5432 with a single DATABASE_URL variable if your app does not already read one.
Step 3: move the database
Create a managed Postgres database on the new platform, then copy the data. Use the custom format so you can restore selectively and in parallel:
# on the old server, or anywhere with network access to it
pg_dump "$OLD_DATABASE_URL" --format=custom --no-owner --no-privileges --file=mvp.dump
# restore into the managed database
pg_restore --dbname="$NEW_DATABASE_URL" --no-owner --no-privileges --jobs=4 mvp.dumpThen verify row counts on your most important tables in both databases before moving on:
SELECT relname, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC
LIMIT 10;n_live_tup is an estimate. Run ANALYZE; on the new database first, or use SELECT count(*) on the two or three tables that matter most.
Step 4: move files to S3-compatible storage
If uploads currently live on the VPS disk, move them to object storage. rclone handles local-to-S3 and S3-to-S3 copies with the same command:
rclone sync /srv/app/uploads newstore:my-bucket/uploads --progressBecause the storage speaks the S3 API, your application code only needs a new endpoint URL and keys.
Step 5: deploy the app and cut over
Deploy your existing Docker image or connect the Git repository, set the environment variables from Step 2, and point the app at the new database and bucket. Test it on the platform's default URL. When it works, stop writes on the old server, run a final database sync, and switch DNS.
What this looks like on Granite.so
Granite.so is built around this unified model. You deploy your app from a Git repository (with automatic builds on push) or from an existing Docker image, attach a managed database, and create S3-compatible storage served through a CDN, all in one dashboard and on one bill. Automatic scaling handles traffic spikes without an autoscaling config.
For an MVP heading toward its first paying users, the budget math is straightforward:
- Hobby, $9/month, includes $15 of monthly usage credits. Enough for a small app, a database, and a bucket while you onboard beta users.
- Advanced, $39/month, for when you need more headroom as usage grows.
Either way, you can state your infrastructure cost before you deploy, and it stays well under an $80 monthly budget for a typical MVP. Check the pricing page for the current details of what each plan includes. Having one dashboard also helps when a non-engineer co-founder needs to check whether the app is up without asking you.
Honest trade-offs of managed services
Managed services are not free of downsides. Know them going in:
- Less low-level control. You typically cannot install arbitrary Postgres extensions, tune every kernel parameter, or get root on the host. Check that the extensions you rely on are supported before migrating.
- Platform dependence. Consolidating on one provider concentrates risk. Mitigate it by sticking to standard interfaces: Docker images, plain Postgres, and the S3 API. If you ever need to leave,
pg_dump,rclone, and your Dockerfile are your exit plan. - Cost at scale. At large, steady workloads, raw infrastructure can be cheaper than managed services. That is a problem worth having, and it is years away for most MVPs.
Checklist for a unified managed stack
- Inventory every service; classify it as code, stateful dependency, or glue
- Move all configuration to environment variables
- Migrate Postgres with
pg_dumpandpg_restore, and verify row counts - Move uploads to S3-compatible storage
- Deploy from Git or Docker image; test on the default URL
- Cut over DNS, keep the old server read-only for a few days, then shut it down
- Test a restore from the managed database's backups before launch
Spend your six weeks on the product
The real benefit of cloud computing and managed services is not any single feature. It is removing the jobs that pull a solo founder away from the product: patching, disk monitoring, backup scripts, and reconciling bills from three vendors. When your app, database, and storage live on one platform, you have one place to look, one number to budget, and nothing to fix at 2 AM.
If you have a working Docker setup and a launch date, Granite.so lets you move it onto one managed platform starting at $9 per month. Bring your image, attach a database, and get back to shipping.