A dashboard for tracking student rosters across online courses. Moved because of arithmetic, not ideology — and delayed by something that had nothing to do with code.
Why they moved
The app worked. That is worth saying first, because it is usually true and usually beside the point. Course curators used it daily to manage student lists. Nothing was broken.
What prompted the move was a calculation. The agency that built it worked out that within about six months the app would cross the platform’s free limits, and their client would be on a monthly subscription from then on — indefinitely, and rising with usage.
Set against that, a one-off migration onto hardware the client already owned and was already paying for stopped being a philosophical question about vendor lock-in and became a spreadsheet.
The ownership benefits came along for free: a database they can back up themselves, and no third party between them and their data.
Scoped before anything was touched
The plan went out first: seven items, an estimate, and an explicit list of what was not included. That last part matters more than it sounds. It is the difference between a handover and an argument.
Work started the same day it was agreed.
What moved
Four services, one compose file.
01
The front end
The React build, containerised and served from the client’s own machine rather than a vendor’s hosting.
02
The scheduled function
A serverless function fired on a schedule by an external cron. It became an ordinary cron service running inside the same server — same job, one less thing owned by someone else.
03
The database
The managed Postgres behind Supabase became a Postgres the client owns, with access moved onto a backend layer instead of being reached through a vendor SDK.
04
The routing layer
Nginx in front of everything, so requests reach the right service — and so the next application on that box does not collide with this one.
Five hours of work, spread over rather longer
This is the part worth being honest about, because it is the part that repeats on almost every job.
The technical work took about five hours. The elapsed time was considerably more, and none of the difference was engineering. We were waiting on SSH keys, and then on the password for the Linux account we needed. Nothing can start until those exist, and they usually live with whoever set the server up originally.
So the useful thing to know before commissioning any migration: the work is short, the access is the long pole. Gather the credentials first and the whole thing compresses into a day.
The one thing that did surprise us
The client’s server was not empty. Other applications were already running on it, and the obvious ports were taken. Not a hard problem — the services were moved to free ports and Nginx routes to them — but exactly the kind of thing that turns into an outage when someone assumes a clean machine and starts a container that fights an existing one.
A migration onto shared infrastructure is a different job from a migration onto a blank server, and it should be treated as one.
The result
The application runs on a server the client owns
The database is theirs, with backups they hold
Database access is enforced by their own backend, not by a vendor SDK
The scheduled job runs on the same machine as everything else
Coexists with the software that was already on that box
Delivered inside the estimate and accepted without revisions
We came to Alexandr to move an application we had built for a client from cloud infrastructure onto the client’s own server. By our estimates the client would have hit the platform’s limits within six months and been pushed onto a monthly subscription.
Alexandr and his team started the same day and delivered exactly to the plan and the deadlines they had given us up front. We’re happy with the work — accepted it straight away, no issues at all. We’ll be coming back to them.
Stasagency partner
What we would tell you honestly
This one went cleanly because the scope was written down before work started. Not every job looks like that. Where an app leans hard on proprietary pieces, a migration turns into a partial rewrite — and we say so before taking the work rather than after.
Get a quote
Same situation?
Tell us what you built and where it runs. You get back whether it can be moved, what the real scope is, and a fixed price — usually within one business day.