Cloud & DevOps

Cloud & DevOps engineering on AWS, Google Cloud and Azure

This is the operational layer underneath an application: the servers it runs on, the pipeline that puts new code there, the alarms that tell a named person when something stops, and the backups you can prove still restore. We set that layer up, document it as code, and keep it uneventful.

What this service covers

  • Cloud platforms: AWS, Google Cloud, Azure
  • CI/CD pipelines: build, test, deploy and roll back
  • Server optimization & monitoring: web server, PHP-FPM, database and cache tuning
  • Alerting: metrics, logs and alerts that reach a person
  • Backups: offsite, monitored and restore-tested

What a deployment pipeline removes

Most teams do not ask for a pipeline. They ask for relief from the three things a missing pipeline costs them: a release that involves dragging files into an FTP client and hoping nothing was missed, a bug that exists only on the server because an extension or an environment variable differs from the laptop it was written on, and an unwritten rule that nobody deploys on a Friday.

Those are one problem wearing three hats. Each is a symptom of the same thing: the steps between a finished commit and a running server live in somebody's head, and get performed slightly differently every time.

What runs on every push

01

Clean checkout

The commit is checked out on a fresh runner, so nothing quietly depends on a file left behind by the last build.

02

Install from the lock file

Dependencies come from the lock file rather than from whatever a cache happens to hold, so everyone gets the same versions.

03

Test, then build once

The suite runs and assets are compiled a single time. A failure here stops the release before it can reach a server.

04

Promote one artifact

The exact build that passed is the one that reaches the server. Nothing is compiled again in production, where it could come out different.

The point of a pipeline is where the failure happens. On a runner, minutes after the push, in front of the person who wrote it — rather than in production, hours later, in front of a customer. Release stops being an event that needs a calm afternoon and becomes something that can happen on a Tuesday morning, twice.

Two things decide whether that holds up. The test suite has to be trusted enough that a red build genuinely stops people, and secrets have to be injected at run time from a secret store rather than committed to the repository to make a build work.

Environments that tell you the truth

A staging environment is only useful if it is wrong in ways you already know about. If staging runs a different PHP or Node version, a different database engine, a different queue driver or a machine a tenth of the size, then a green result on staging proves very little.

We build staging from the same definition as production and let it differ on purpose, in a short written list rather than by accident.

Deliberate differences between production and staging
Aspect Production Staging
Machine size Sized for real traffic Smaller — enough to run the app, not to load-test it
Credentials Live keys and live accounts Sandbox or test-mode keys only
Data Real customer data A scrubbed copy, or generated data
Outbound mail and SMS Delivered to customers Caught by a single test inbox
Who can reach it The public Behind HTTP auth or an IP allowlist, and kept out of search results

Everything else is held identical — runtime versions and extensions, cache and queue drivers, web server and PHP-FPM configuration, cron entries, and the deploy process itself. The point is that the release going to production has already been rehearsed on something that behaves like production.

Every unlisted difference is a place where a green staging run means nothing. Where one cannot be removed — a payment provider with no sandbox for a particular flow, an integration with hardware that exists in exactly one place — we say so and test that part deliberately in production behind a feature flag, rather than pretending staging covered it.

Infrastructure as code, and why click-configured servers rot

A server assembled by hand in a web console is a server nobody can rebuild. Which firewall rule was opened for that one payment integration, which cron entry was added at two in the morning, why the swap file is that size, which package was installed from a third-party repository — all of it lives in one person's memory and leaves when they do.

Cannot reproduce it quietly becomes cannot move it. Six months later nobody is willing to touch the machine, so it does not get patched, does not get resized, and does not get handed to the next person with any confidence.

We describe infrastructure in code instead: the cloud resources declared in version-controlled definition files, and provisioning scripts or images for what runs inside them. The environment becomes a file in your repository, reviewed like any other change, with a history that explains why each rule exists. Rebuilding a machine after an incident, adding a second one, or standing up a separate region for a client with data-residency obligations turns into an ordinary task rather than an archaeological dig.

On a small project this stays deliberately modest: one definition per environment, a variables file for the handful of things that differ, and a plan output reviewed in the pull request before anything is applied. You do not need a platform team to get the benefit.

Zero-downtime deploys, and a rollback you have actually used

Copying files into a directory that is currently being served creates a window in which half the old code and half the new code are live at the same time. We use atomic releases: the new build is prepared alongside the running one, and the switch is a symlink change followed by a reload, so requests already in flight finish on the old release and the next ones land on the new. Queue workers are restarted after the switch, not before, so no job is picked up by code that is about to disappear.

Rollback matters more than the deploy. Keeping the previous releases on disk makes reverting a matter of pointing the symlink back and reloading, which takes seconds.

The database is the harder half, because a migration that has already dropped a column cannot be undone by a symlink. Where it counts we separate schema changes from code changes, so the code can move backwards even when the schema cannot.

Atomic releases

Build prepared beside the live one; the cutover is a symlink switch and a reload.

Kept releases

Previous builds stay on disk, so reverting is a routine step rather than a rebuild.

Reversible migrations

Destructive schema changes are split across releases so code can roll back alone.

Worked example: retiring a column without a maintenance window

  1. Release one. Add the new column. Deploy code that still reads the old one but writes to both.
  2. Release two. Backfill the existing rows with a background job, in batches, while the site keeps serving.
  3. Release three. Switch reads to the new column. The old one is still populated, so this release can be reverted on its own.
  4. Release four. Once reverting is no longer plausible, drop the old column.

Four deploys instead of one, and at no point does a version of the code exist that cannot be reversed by pointing the symlink back. The cost is planning; the alternative is a restore while customers wait.

Monitoring, alerting and logs are three different jobs

They get sold as one word, and the difference shows up at the worst possible moment.

  • Monitoring is the current state. CPU, memory, disk, queue depth, response times, certificate expiry, and whether the site answers at all from outside your own network.
  • Logging is what happened. Application errors with stack traces and request context, web server access logs, slow query logs — retained long enough to investigate a problem that gets reported a week late.
  • Alerting is the narrow part that interrupts a human being. It is the only one of the three that costs you something when it is wrong in either direction.

An alert nobody acts on is not monitoring — it is noise with a delivery mechanism. A channel full of routine warnings teaches everyone to swipe them away, and the one alert that mattered gets dismissed with the rest.

So we keep the alert set deliberately small and make each one answerable: it fires on a symptom a user would actually notice, it reaches a named person or an on-call rotation rather than a shared inbox, and it carries enough context — which host, which endpoint, which error, what changed recently — for someone to start work without opening five dashboards.

Anything that fires regularly and needs no action is either fixed or deleted, because leaving it there is worse than having no alert at all.

If you only ever have one check, make it an external one: a request to a real page, from outside your own network, on a short interval. An unreachable machine is in no position to report that it is unreachable, and an expired certificate or an edited DNS record looks perfectly healthy from the inside.

Backups that have been restored at least once

A backup nobody has restored is a belief, not a safeguard. They fail in dull, common ways:

  • A job that has been erroring silently since a credential rotated, with nobody watching the job itself.
  • A dump that captures the database but not the uploaded files.
  • A snapshot schedule covering the boot volume but not the data volume that was attached later.
  • A retention window shorter than the time it takes anyone to notice the corruption.
  • An archive sitting on the same machine it was meant to protect.

We back up to storage separate from the server, monitor the backup job itself so a silent failure raises an alert like any other symptom, and restore into a scratch environment so two things are known rather than assumed: that the data comes back intact, and roughly how long a real recovery would take.

Two numbers decide what a backup is worth: how much data you can afford to lose, which is the gap between copies, and how long you can afford to be down while one is restored. Agree both before choosing a schedule. The schedule is the cheap part; the restore is not.

That second number is what turns a vague promise into a recovery time you can plan a business around, and it is usually the one nobody has measured.

One design point follows from it: ransomware and a table deleted by mistake are the same problem from the backup's point of view. Both need a copy the compromised account could not have written to — a separate credential without delete rights, and versioned storage where the provider offers it, rather than a nightly job holding full permissions over its own archive.

Autoscaling, and what it does not fix

Autoscaling adds and removes instances behind a load balancer as demand moves. It is the right answer for traffic that genuinely varies — a campaign, a seasonal peak, a service that is quiet overnight — and for surviving the loss of a single machine without anyone noticing.

It is an availability answer, not a performance one. A query with no index, an N+1 loop on a product listing or a report that reads an entire table stays exactly as slow on twenty instances as on one, and now you pay twenty times over to be slow.

It also assumes the application can scale horizontally at all. Sessions and uploaded files have to live somewhere shared rather than on local disk, scheduled tasks must not run once per instance, and the database has to absorb the extra connections that arrive with each new node.

We look at the profile before the instance count, and the cheaper answer is often an index, a cache and a queue — the application-side groundwork described on our web development page.

Cloud cost control

Cloud bills grow by accretion, and the usual sources are repetitive enough to check against a list:

  • Instances sized for a launch spike that never came back down.
  • Volumes and snapshots left behind by machines terminated months ago.
  • Load balancers, NAT gateways and static addresses attached to nothing.
  • Logs kept at full verbosity forever in the most expensive storage class.
  • Egress — the charge for data leaving the provider, which hides well because no line on the invoice is named after the feature causing it.

Before any of that, one sanity check: open the provider's cost report grouped by service for the last three months and find the largest line. It is often not the one people assume, and tidying the small lines first is how a cost review ends up costing more in effort than it saves.

Then we tag resources so every line has an owner, right-size against measured usage rather than the original guess, apply lifecycle rules to logs, snapshots and backups, and put static assets behind a CDN so origin egress falls. Committed-use or reserved pricing comes last, once the shape of the usage is stable, because committing to the wrong size for a year is an expensive way to save money.

When moving off a single VPS is premature

There is a version of this work that costs more than it returns. One application with modest traffic, a single database and a handful of background jobs runs perfectly well on one well-configured virtual server that has backups, monitoring and a deployment pipeline.

Splitting it across a managed database, a managed cache, container orchestration, a message broker and a service mesh adds moving parts, a monthly bill and a category of failure that needs someone on hand who understands it.

What you accept in return is worth naming out loud: patching or rebooting that one machine is visible to users unless a window is planned, and recovery from its total loss is a rebuild plus a restore rather than a failover. If both are tolerable this year, the simpler setup is cheaper in money and in attention.

Managed services earn their place at identifiable moments: when losing the database would take longer to recover than the business can tolerate, when the server can no longer be patched or rebooted without visible downtime, when several teams need to deploy without queueing behind each other, or when a contract requires isolation and audit trails you cannot produce by hand.

A single server is often still the right answer. We are content to say so — and to build it so that the eventual move is a change to a definition file rather than a rewrite.

How we engage

Three ways this work usually starts

Review

We go through the servers, deploy process, backups, alerts and bill as they stand, and hand back a prioritized list with what each item would cost to fix. Quoted per project.

Build or migrate

New environments on AWS, Google Cloud or Azure, a CI/CD pipeline, infrastructure as code, or a planned move from an existing host with a rehearsed cutover. Quoted per project.

Ongoing care

Patching, certificate renewals, backup checks, alert triage and small changes. Our maintenance plans start at $49 per month; heavier retainers are quoted on scope.

What we need from you to start

None of it is unusual, but gathering it early is the difference between a review that moves and one that stalls waiting on access.

  • Access you can revoke: a named user in the cloud console and an SSH key of our own, rather than a shared root login sent over email.
  • The repository, and whoever deploys today: half an hour with the person who currently pushes to production usually explains more than the server does.
  • Control of the domain and DNS: cutovers need record changes and a lowered TTL ahead of time, and that happens in your registrar account.
  • Someone who can approve a downtime window: even a short one, and even if we end up not needing it.
  • A list of what talks to the system from outside: payment webhooks, an accounting sync, a partner's IP allowlist. These are the things that break quietly after a move.

The same list works in reverse at the end of an engagement. We write down how the environment is deployed, patched and restored, walk somebody on your side through it, and ask you to remove our access — and nothing we build depends on an account of ours continuing to exist.

All prices in US dollars (USD). We can invoice in GBP, EUR, AUD or INR on request.

Working hours: Mon–Fri, 09:00–19:00 IST (UTC+5:30) — 4+ hours of live overlap with US Eastern; full overlap with UK and EU mornings.

Cloud & DevOps FAQs

Cloud hosting is where the application runs. DevOps is how code reaches it and how you learn that it broke: the build and test pipeline, the environment definitions, the deploy and rollback process, backups, monitoring and alerting. Hosting can be bought on its own, but without the second half every release and every incident is handled by hand.

No. Most engagements start on whatever is already running, including a single VPS or a shared host, and the first thing we produce is a review rather than a migration plan. We propose a move only when there is a concrete reason for one: a platform that cannot give you usable backups, a host with no way to run a staging copy, a machine that cannot be patched without downtime, or a data-residency requirement.

Often not, at least not yet. Kubernetes pays for itself when several teams deploy independently, when varied workloads make bin-packing worth real money, or when identical behavior across clouds is a requirement. For one application with a database and some background jobs it adds an operational surface somebody has to own. We would rather start simpler and move when a concrete reason appears.

Usually yes. We build the new environment alongside the old one, replicate the database, copy uploaded files, run both in parallel while we test, then cut over DNS with a lowered TTL and, if the database requires it, a short read-only window. The rollback path is agreed before the cutover rather than improvised during it.

We tag resources so every invoice line has an owner, right-size instances against measured usage, remove orphaned volumes, snapshots and idle load balancers, apply lifecycle rules to logs and backups, and put static assets behind a CDN to cut egress. Reserved or committed pricing comes last, once usage has a stable shape.

Previous releases stay on the server, so reverting is a symlink change and a reload rather than a rebuild. Database migrations are the harder half, which is why destructive schema changes are split across releases so code can be rolled back on its own. Alerting is meant to tell us before a customer does.

Yes, because a backup nobody has restored is only an assumption. We back up to storage separate from the server it protects, monitor the backup job so a silent failure raises an alert, and restore into a scratch environment to confirm the data returns intact and to measure how long a genuine recovery would take.

Monitoring and alerting run continuously once they are in place, and alerts can be routed to reach whoever you choose at any hour, including your own on-call rotation. Somebody on our side picking up an alert in the middle of the night is a separate commitment, and one that belongs in a written retainer rather than in an assumption. Our standard hours are Mon–Fri, 09:00–19:00 IST (UTC+5:30).

For most applications all three are capable, and the deciding factors are practical: existing credits or agreements, where your team already has experience, data-residency obligations, and which managed services you genuinely plan to use. We define the environment as code, so the choice stays documented and reversible instead of living in one person's console history.

Ping us about your infrastructure

Tell us what is running, and where it hurts

Send us the shape of the setup — cloud provider or bare server, how you deploy today, what wakes people up — and we will come back with what we would change first and what it would cost. If the honest answer is that your current server is fine, we will say so.

Related services

Web Development

Fast, scalable websites and web apps to develop web application solutions with Laravel, React and modern stacks.

Mobile App Development

Native and cross-platform iOS & Android apps built by the best app developers, with React Native and Flutter.

Custom Software

Custom software development for bespoke CRM, ERP and SaaS platforms, engineered around your exact workflow.