Buying Guide15 min read

What Does It Cost to Maintain a Web Application?

By Niraj Jha ·

Co-Founder & CTO · Last updated

Key takeaways

  • Budget 15-20% of the original build cost per year for maintenance. A $40,000 build costs roughly $6,000-$8,000 a year to keep current.
  • Runtime and framework end-of-life dates are published in advance, which makes them the easiest part of maintenance to plan and the most avoidable to be caught by.
  • Automation like Dependabot and npm audit raises the alarm; it does not do the work. Someone still has to judge, upgrade and verify.
  • Deferred maintenance does not save money - it converts a predictable expense into an unpredictable one, and upgrades get harder superlinearly.
  • A retainer should name what it excludes. New features are not maintenance, and a retainer that absorbs them silently runs out of hours for the actual work.

The launch invoice is the number everyone plans for. The one that catches people out arrives quietly, a year later, when a dependency goes end-of-life, a payment provider deprecates an API version, or a security advisory lands on a package buried four levels down in the tree.

Software is not a building. It does not sit there, finished, slowly weathering. It sits inside an ecosystem that keeps moving underneath it - browsers ship, runtimes reach end of support, certificates expire, platforms change their terms. Doing nothing is a decision with a cost, and the cost compounds.

This is what maintaining a web application actually involves, what it costs, and how to tell the difference between a maintenance retainer that is earning its money and one that is not.

The short answer

Budget 15 - 20% of the original build cost per year to keep a web application current, secure and supported.

Original buildAnnual maintenance (typical)What that buys
$10,000$1,500 - $2,000Patching, uptime monitoring, small fixes
$40,000$6,000 - $8,000The above plus dependency upgrades and minor changes
$100,000$15,000 - $20,000The above plus performance work and an incident response target
$250,000+$37,000+Dedicated retained capacity

That range is not a market convention someone invented. It falls out of what the work actually is, which is worth understanding so you can judge a quote instead of accepting one.

If your budget is exactly the build price and nothing else, the budget is short. Not because anyone is upselling you, but because year one of owning software has recurring costs the same way year one of owning a vehicle does.

What maintenance actually consists of

Security patching, which is not optional

New vulnerabilities are published continuously to the National Vulnerability Database. A typical web application depends on hundreds of packages transitively, and any of them can be the one that gets an advisory.

The tooling for this is mature and mostly automated - npm audit surfaces known issues, Dependabot opens the pull requests - but automation raises the alarm, it does not do the work. Someone has to judge whether an advisory affects your usage, apply the upgrade, and confirm nothing broke. That judgement is the job.

Runtime and framework end-of-life

This one has published dates, which makes it the easiest part of maintenance to plan and the most embarrassing to be caught by.

Node.js runs a fixed release schedule: each even-numbered major line becomes LTS, gets active support, moves to maintenance, and then stops receiving fixes entirely. endoflife.date tracks the calendar. Once a runtime is past end of life, it stops getting security patches - and hosting platforms eventually refuse to run it at all.

Framework majors work similarly. Staying roughly current is a small, regular cost. Skipping four major versions and then attempting the jump is a project, not a patch.

Third-party APIs moving without asking

Payment providers, auth providers, mapping services and mail platforms version their APIs and retire old ones. Stripe's documentation is a good example of a vendor that manages this carefully - and even careful vendors deprecate. The work is small if you do it in the window and urgent if you do not.

The browser and platform floor moving

Google's performance thresholds are a moving, measured target rather than a one-off launch task. Core Web Vitals metrics get added and refined - Interaction to Next Paint replaced First Input Delay as the responsiveness metric - and your field data changes as your content and traffic change. A site that passed at launch can quietly stop passing.

Actual bugs, found by actual users

No amount of testing finds everything, because real users do things nobody modelled. A steady trickle of small defects is normal and healthy. It is only a warning sign when the trickle does not slow down after the first few months.

Backups you have actually restored

An untested backup is a belief, not a backup. Part of a real maintenance arrangement is periodically restoring one somewhere safe and confirming the data comes back.

What a maintenance retainer should include

Ask for these explicitly. A retainer that does not name them is selling you availability, not maintenance.

ItemWhat good looks like
Security patchingAdvisories triaged within a stated window, critical ones same week
Dependency upgradesRegular cadence, not a big-bang once a year
Runtime/framework currencyA plan with dates, tied to published EOL schedules
Uptime monitoringAlerting to a human, with a response target
Error monitoringExceptions captured and triaged, not just logged
BackupsAutomated, offsite, and restore-tested on a schedule
Bug fixesDefects in delivered work, with a defined severity scale
A monthly reportWhat was patched, what broke, what is coming

And be clear about what it excludes. New features are not maintenance. Neither is a redesign, a new integration, or work caused by a change in your own business process. Those are projects, quoted separately. A retainer that quietly absorbs feature work either runs out of hours for the actual maintenance or is priced as though it will.

The cost of not doing it

Deferred maintenance does not save money, it converts a predictable expense into an unpredictable one.

Upgrades get harder superlinearly. Moving one major version is routine. Moving four at once means every breaking change interacts with every other, on a codebase nobody has touched in two years.

The security window widens. The gap between an advisory being published and your patch being applied is your exposure, and published advisories are exactly what opportunistic scanning looks for. The OWASP Top 10 has included vulnerable and outdated components for years because it keeps being how systems get compromised.

Hiring gets harder. Engineers can work on an old stack. Fewer want to, and the ones who will charge more.

Small changes stop being small. The clearest symptom that debt has accumulated is that estimates become unpredictable - a change that sounds trivial keeps turning into a week.

The expensive version of this story is always the same: an application runs untouched for three years, then a compliance requirement or a broken integration forces the issue, and the only viable path is a rebuild. The rebuild costs more than five years of maintenance would have.

Retainer, ad hoc, or in-house

Retainer. Fixed monthly fee for a defined scope and response target. Predictable for you, and it means someone is watching before something breaks. Best for systems the business depends on. Downside: you pay in quiet months.

Ad hoc / time and materials. You pay only when something needs doing. Cheaper in the quiet months and genuinely fine for low-stakes internal tools. Downside: nobody is watching, so you find out about problems from users, and you queue behind whoever has a retainer.

In-house. Makes sense once you have enough software to occupy someone. A single application rarely does - you end up with an engineer whose job is 15% maintenance and 85% waiting, which is bad value and bad for them.

A reasonable pattern for one significant application: a modest retainer covering patching, monitoring and small fixes, with feature work quoted separately as it comes up.

Reducing what maintenance costs you

Most of this is decided during the build, not after it.

  • Fewer dependencies. Every package is a small permanent liability. A library that saves an afternoon and adds a transitive tree of forty is a bad trade.
  • Boring, well-supported technology. Popular tools get patched quickly and have people who can work on them. The Stack Overflow Developer Survey is a rough guide to what you will be able to hire for.
  • Real tests. The thing that makes an upgrade a two-hour job rather than a two-week one is a suite that tells you immediately whether the upgrade broke anything.
  • Automated pipelines. If deploying is a documented, one-command operation, patching is cheap. If it is a ritual only one person knows, everything is expensive. The Twelve-Factor App is still the clearest short summary of what makes a service operable by someone who did not build it.
  • Semantic versioning discipline. SemVer is what lets you distinguish a safe patch from a breaking change at a glance, and it only helps if your own internal packages honour it.
  • Upgrade little and often. A monthly hour beats an annual week.

Questions to ask before you sign a retainer

  1. What exactly is included, and what is explicitly excluded?
  2. What is your response target for a critical security issue? For a normal bug?
  3. How often do you apply dependency updates?
  4. What is the plan for the next framework and runtime major version, with dates?
  5. Do you monitor uptime and errors, and who gets alerted?
  6. When did you last test-restore a backup?
  7. What do I receive each month that shows the work happened?
  8. What is the notice period, and what happens at the end?

That last one matters. A maintenance relationship you cannot leave without losing access to your own systems is not a service, and it is worth confirming that repositories and cloud accounts remain in your name - the same point we make in who owns the code.

A year in the life of a maintained application

What the retainer actually buys, month by month. This is a real-shaped year for a mid-sized product, not a worst case.

MonthWhat happenedEffort
1Two dependency advisories, one affecting us. Patched and deployed.3 hrs
2Quiet. Routine dependency bump, monitoring review.2 hrs
3Payment provider announced API version sunset in 9 months. Planned.2 hrs
4Framework minor upgrade. One deprecation warning fixed.5 hrs
5User-reported bug in a rarely used export. Fixed.4 hrs
6Backup restore test. Found the restore script assumed an old schema.6 hrs
7Payment API migration done, well ahead of the deadline.9 hrs
8Quiet.2 hrs
9Traffic spike exposed a slow query. Added an index.5 hrs
10Node major version planning; upgrade scheduled.3 hrs
11Node upgrade, full regression, deployed.11 hrs
12Annual review, dependency audit, capacity check.5 hrs

Total: about 57 hours across the year. Which at a normal rate lands close to the 15 - 20% figure for a build in the $40,000 range.

Two things are worth pulling out of that table.

Month 6 is the whole argument for retainers. The backup had been running successfully for a year. It was also unrestorable, because the restore script referenced a schema that had since changed. Nobody would ever have found that except by testing it, and you find it either in a scheduled test or during an actual incident.

Month 7 was cheap because of month 3. The API sunset was handled in a planned nine-hour block instead of an emergency. The same work under deadline pressure, with no time to test properly, is two to three times the effort and carries real risk of breaking payments.

Auditing an application nobody has maintained

If you have inherited something, or yours has been running untouched, this is the order to look in. Most of it takes a day.

1. What runtime is it on, and is that version still supported? Check against endoflife.date. This is the single most important answer, because a runtime past end of life means no security patches at all, regardless of how careful you are with everything else.

2. Run a dependency audit. npm audit gives you the count and severity in seconds. Read the number of critical and high advisories, and how old the oldest one is.

3. How far behind is the framework? One major version is routine. Three or more and the upgrade is a project.

4. Do backups exist, and has one ever been restored? Ask specifically for the date of the last successful restore test. "We have backups" is not an answer to that question.

5. Is anything monitored? If a page started returning errors right now, how would you find out? If the answer is "a customer would email us", that is the first thing to fix and it is cheap.

6. Can it be deployed? Have someone attempt a deploy of an unchanged version. Surprisingly often this fails, and finding out during an emergency is the bad time to learn it.

7. Are the SSL certificate and domain on auto-renew, in your name?

The result that should worry you most is not a long list of vulnerabilities. It is being unable to deploy. A system you cannot deploy cannot be patched, which means every future problem is an emergency regardless of how small it would otherwise have been.

What an audit typically costs to act on

Rough shape of remediation, once you know where you stand.

FindingTypical effort to fix
Runtime one LTS behind1 - 3 days
Runtime past end of life5 - 15 days
Framework one major behind2 - 5 days
Framework three or more majors behind15 - 40 days, consider a rewrite of parts
No CI/CD, manual deploys3 - 5 days to automate
No monitoring or alerting1 - 2 days
Backups untested1 day to test, unknown to fix
No test coverage on critical paths5 - 15 days for a useful baseline

The pattern is the familiar one: each row roughly triples in cost as it ages. Being one version behind is a task. Being three versions behind is a project.

Objections we hear

"It works fine. Why would I pay for maintenance?" Because "works fine" describes the application, and the risk is in the ecosystem around it: the runtime approaching end of life, the payment API being sunset, the advisory published last week against a package four levels deep. None of those change how the app behaves today, and all of them change what next year costs. Maintenance is not fixing a thing that is broken; it is preventing the state where fixing it is a project.

"Can we just do it once a year in one go?" You can, and it is more expensive than doing it monthly. Twelve months of accumulated upgrades interact with each other, so debugging is harder, and you have carried twelve months of security exposure to save a few hours of attention.

"Our developer says the code is fine and does not need touching." Their code may well be fine. Maintenance is mostly not about their code - it is about the hundreds of dependencies underneath it, which are being changed by other people continuously.

"Can we do it in-house?" If you have an engineer, yes, and it is a good use of a few hours a month. The failure mode is that maintenance is never urgent, so it loses every week to something that is. Whoever owns it needs scheduled time, not spare time.

"What if we plan to rebuild in two years anyway?" Then maintain at a reduced level: security patches and monitoring, skip the framework upgrades. Say that out loud and price it deliberately, rather than drifting into it. And be honest about whether "we will rebuild in two years" is a plan or a way of deferring the decision, because the second one is how applications reach the state where a rebuild is the only option left.

"How do I know the retainer is being used?" Ask for a monthly summary: what was patched, what broke, what is coming. If the answer three months running is "nothing to report", either your application is unusually quiet or nobody is looking. Both are worth knowing.

What a good monthly report looks like

The retainer is invisible work. A short monthly report is what makes it accountable, and it should take the supplier fifteen minutes to produce.

MAINTENANCE REPORT - March

SECURITY
  Advisories reviewed:        7
  Affecting us:               2 (1 high, 1 moderate)
  Patched:                    2, deployed 4 and 11 March
  Outstanding:                0

DEPENDENCIES
  Packages updated:           14 minor, 3 patch
  Deferred:                   1 (major version, scheduled April)

PLATFORM
  Runtime:                    Node 22 LTS, supported until April 2027
  Framework:                  current major, one minor behind

AVAILABILITY
  Uptime:                     99.98%
  Incidents:                  1 (3 min, upstream provider)
  Slowest endpoint:           /reports/export, p95 1.9s

BACKUPS
  Automated:                  daily, offsite
  Last restore test:          18 March, passed

WORK DONE
  Fixed CSV export encoding bug (reported 6 March)
  Added index on orders.created_at after slow query alert

COMING UP
  April: framework major upgrade, ~1 day
  June: payment API v2 migration, deadline September

If you receive that every month, you can tell at a glance whether the money is doing anything. If you receive an invoice and nothing else, you cannot.

Maintenance for a system you did not build

Taking over someone else's application is a legitimate and common engagement, and it is priced differently for a reason.

Expect a paid audit first. Nobody responsible will quote a maintenance retainer on a codebase they have not opened. A one to three day audit produces the findings list from the section above, and that is what the retainer gets priced against.

Expect the first three months to cost more. There is a backlog: outstanding advisories, missing monitoring, no deployment automation, absent tests around critical paths. Clearing that is remediation, not maintenance, and it should be quoted separately so you can see what is one-off and what is ongoing.

Expect some things to be declined. A responsible supplier will say "we will maintain this, but we will not guarantee response times on the payments module until it has tests". That is a good answer.

The steady-state rate afterwards is usually higher than for a system the supplier built, because familiarity is worth real money. Roughly 20 - 25% of estimated rebuild value rather than 15 - 20%, until the codebase has been brought up to a standard they are comfortable with.

How we do it

Every build ships with the post-launch support window stated in its scope, covering defects in our work. Beyond that, maintenance is a separate retainer with a named scope and response targets, because bundling it invisibly into a build price is how it ends up not happening.

We keep the dependency surface small deliberately, wire security scanning into the pipeline on day one, and put repositories and cloud accounts in your name from the first deploy, so leaving is always possible.

If you have an application that has been running untouched for a while and you want an honest read on where it stands, tell us about it. For the build-side numbers, see what a web application costs.

Related reading

Sources and further reading

Article FAQ

Questions,
answered

More on What Does It Cost to Maintain a Web Application? - the follow-ups we get asked most, answered the way we would answer them on a call.

Typically 15 to 20% of the original build cost. That covers security patching, dependency and runtime upgrades, uptime and error monitoring, backups, and fixes for defects in delivered work.

Have a product to build?

Shunya ships production software - web applications end to end - with one team that owns the whole stack from concept to launch. Tell us what you want to build.

Niraj Jha

Written by

Niraj Jha

Co-Founder & CTO

Co-Founder & CTO of Shunya Tech. Full-stack architect who sets the engineering culture and technical standards behind every product we ship - from database design to production delivery on Next.js, tRPC, and Prisma.

Last updated