Services

2026 03 20 Per Service Pricing


Per-Service Pricing Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the single global /pricing page with dedicated, fine-grained pricing sections embedded directly on each service's marketing page, featuring hourly rates for DevOps/consulting services, monthly managed rates for infrastructure, and an interactive calculator for managed infra.

Architecture: Centralize all pricing data in lib/service-pricing-data.ts, inject a ServicePricingSection component into the service page template (_pages/services/[slug].tsx), and build a ManagedInfraCalculator for database/ops service pages. Remove the global Pricing nav link and per-dropdown Pricing links from data/nav.tsx.

Tech Stack: Next.js 15, React, TypeScript, Tailwind CSS, Lucide React, ui package (Button, cn)


File Map#

ActionFileResponsibility
Modifyapps/www/data/nav.tsxRemove top-level Pricing link + all dropdown Pricing entries
Createapps/www/lib/service-pricing-data.tsAll service pricing configs (hourly / monthly tiers / managed infra rates)
Createapps/www/lib/managed-infra-pricing.tsCalculator logic for managed infra (pure functions, testable)
Createapps/www/components/Pricing/ServicePricingSection.tsxPricing section rendered on service pages
Createapps/www/components/Pricing/ManagedInfraCalculator.tsxInteractive calculator for managed infra services
Modifyapps/www/_pages/services/[slug].tsxInject ServicePricingSection after ComparisonTable
Modifyapps/www/app/pricing/PricingContent.tsxConvert to hub page linking to per-service pricing
Modifyapps/www/app/pricing/page.tsxUpdate metadata

Pricing Model Reference#

DevOps & SRE Services (hourly rate + monthly retainer)#

  • DevOps as a Service: $120/hr, min 40 hr/month retainer ($4,800/mo)
  • SRE as a Service: $130/hr, min 40 hr/month retainer ($5,200/mo)
  • DevOps Emergency: $200/hr, min 4 hours, 24/7 response
  • DevOps Trainings: $150/hr per trainer (standard = 16h, up to 10 people)

Cloud Account Management (monthly)#

  • Starter: $1,500/mo (up to 2 accounts) — keep existing
  • Professional: $3,500/mo (up to 10 accounts) — keep existing
  • Enterprise: Custom

Managed Kubernetes (monthly)#

  • Keep existing tiers ($3,000 / $6,000 / Custom)

Managed Infrastructure — Databases (monthly managed)#

ServiceSmallMediumLarge (HA)
PostgreSQL$400$800$1,600
MySQL$350$700$1,400
MongoDB$600$1,200$2,400
Redis$200$350$600
Kafka$800$1,600Custom
OpenSearch$500$1,000$2,000

Managed Infrastructure — Ops (monthly)#

ServiceBaseDetails
Runners$50/runner/momin 5 runners ($250/mo)
Prometheus$400HA: $700
Artifactory$300Docker/OCI repositories included where scoped
Artifact Repos$400
DNS$200up to 10 zones; $400 unlimited
Certificates$150up to 20 certs; $300 unlimited

Consulting & Advisory (project / hourly)#

ServiceRateNotes
Technology Consulting$120/hr
Security Audit$4,800 fixed~4 days
Infrastructure Audit$3,200 fixed~3 days
CI/CD Audit$2,400 fixed~2 days
Solution for SaaS/PaaS$120/hrmin 20 hrs

Productivity Suites (per-user/mo)#

ServiceRateMinSetup
Google Apps Management$8/user/mo10 users ($80/mo)$800
Microsoft 365 Management$9/user/mo10 users ($90/mo)$900
Zimbra (Sovereign Productivity)$6/mailbox/mo25 mailboxes ($150/mo)$600

Task 1: Remove Pricing from Navigation#

Files:

  • Modify: apps/www/data/nav.tsx

  • Step 1: Remove top-level Pricing menu item

In getMenu(), remove:

typescript
1
{
2
title: 'Pricing',
3
url: '/pricing',
4
},
  • Step 2: Remove Pricing links from devopsGroups

Remove the entry { text: 'Pricing', url: '/docs/services/pricing', ... } from devopsGroups[0].links.

  • Step 3: Remove Pricing links from cloudGroups

Remove the Pricing entry from cloudGroups[0].links (the Cloud group).

  • Step 4: Remove Pricing links from managedGroups

Remove the Pricing entry from managedGroups[1].links (the Observability & Ops group).

  • Step 5: Remove Pricing links from consultingGroups

Remove the Pricing entry from consultingGroups[1].links (the Advisory group).

  • Step 6: Update getFooterServicesLinks to remove Pricing

Change the footer helper — remove { text: 'Pricing', url: '/pricing' } from the returned array.

  • Step 7: Commit
bash
1
git add apps/www/data/nav.tsx
2
git commit -m "feat: remove global Pricing nav link — pricing now lives on service pages"

Task 2: Create Centralized Pricing Data#

Files:

  • Create: apps/www/lib/service-pricing-data.ts

  • Step 1: Create the file with types and data

typescript
1
// apps/www/lib/service-pricing-data.ts
2
3
export type = 'hourly' | 'monthly-tiers' | 'managed-infra' | 'per-user' | 'fixed-project'
4
5
export interface {
6
: 'hourly'
7
: number // $ per hour
8
?: number // minimum engagement hours
9
?: number // pre-calculated retainer if applicable
10
: string
11
}
12
13
export interface {
14
: 'monthly-tiers'
15
: {
16
: string
17
: number | 'custom'
18
: string
19
: string
20
?: boolean
21
: { : string; : boolean }[]
22
: { : string; : string }
23
}[]
24
}
25
26
export interface {
27
: 'per-user'
28
: number // $ per user per month
29
: number
30
: number
31
: string // e.g. "users", "mailboxes"
32
: string
33
}
34
35
export interface {
36
: 'fixed-project'
37
: number
38
: string
39
: string[]
40
: { : string; : string }
41
}
42
43
export type =
44
|
45
|
46
|
47
|
48
49
export const : <string, > = {
50
'devops-as-a-service': {
51
: 'hourly',
52
: 120,
53
: 40,
54
: 4800,
55
: 'Dedicated DevOps engineers embedded in your team. Billed hourly with a 40-hour monthly minimum.',
56
},
57
'sre-as-a-service': {
58
: 'hourly',
59
: 130,
60
: 40,
61
: 5200,
62
: '24/7 reliability engineering. On-call, incident response, and proactive hardening.',
63
},
64
: {
65
: 'hourly',
66
: 200,
67
: 4,
68
: 'Immediate senior engineer response for production incidents. Available 24/7, billed in 4-hour blocks.',
69
},
70
'devops-trainings': {
71
: 'hourly',
72
: 150,
73
: 'Expert-led hands-on training. Standard curriculum is 16 hours (up to 10 people per session).',
74
},
75
'cloud-account-management': {
76
: 'monthly-tiers',
77
: [
78
{
79
: 'Starter',
80
: 1500,
81
: '/mo',
82
: 'Small teams managing 1-2 cloud accounts with basic governance needs.',
83
: [
84
{ : 'Up to 2 cloud accounts', : true },
85
{ : 'Monthly cost review', : true },
86
{ : 'Basic IAM policies', : true },
87
{ : 'Email support', : true },
88
{ : 'Multi-cloud support', : false },
89
{ : 'Dedicated engineer', : false },
90
{ : 'Compliance reporting', : false },
91
],
92
: { : 'Get started', : '/contact' },
93
},
94
{
95
: 'Professional',
96
: 3500,
97
: '/mo',
98
: 'Growing teams with multiple accounts and active cost optimization.',
99
: true,
100
: [
101
{ : 'Up to 10 cloud accounts', : true },
102
{ : 'Weekly cost optimization', : true },
103
{ : 'Advanced IAM & network segmentation', : true },
104
{ : 'Slack + email support', : true },
105
{ : 'Multi-cloud support', : true },
106
{ : 'Dedicated engineer', : true },
107
{ : 'Compliance reporting', : false },
108
],
109
: { : 'Get started', : '/contact' },
110
},
111
{
112
: 'Enterprise',
113
: 'custom',
114
: '',
115
: 'Organizations with complex multi-cloud estates and compliance requirements.',
116
: [
117
{ : 'Unlimited cloud accounts', : true },
118
{ : 'Continuous cost optimization', : true },
119
{ : 'CIS benchmarks & audit-ready reporting', : true },
120
{ : 'Dedicated Slack + phone support', : true },
121
{ : 'Multi-cloud support', : true },
122
{ : 'Dedicated engineer team', : true },
123
{ : 'SOC2/HIPAA compliance reporting', : true },
124
],
125
: { : 'Contact us', : '/contact' },
126
},
127
],
128
},
129
'managed-kubernetes': {
130
: 'monthly-tiers',
131
: [
132
{
133
: 'Starter',
134
: 3000,
135
: '/mo',
136
: 'Teams running their first production K8s cluster.',
137
: [
138
{ : '1 production cluster', : true },
139
{ : 'HA control plane', : true },
140
{ : 'Basic monitoring & alerting', : true },
141
{ : 'Email support', : true },
142
{ : 'GitOps deployment', : false },
143
{ : 'Multi-cluster management', : false },
144
{ : 'Custom platform engineering', : false },
145
],
146
: { : 'Get started', : '/contact' },
147
},
148
{
149
: 'Professional',
150
: 6000,
151
: '/mo',
152
: 'Production workloads needing full-stack platform engineering.',
153
: true,
154
: [
155
{ : 'Up to 3 clusters', : true },
156
{ : 'HA control plane + autoscaling', : true },
157
{ : 'Full observability stack', : true },
158
{ : 'Slack + email support', : true },
159
{ : 'GitOps deployment', : true },
160
{ : 'Multi-cluster management', : true },
161
{ : 'Custom platform engineering', : false },
162
],
163
: { : 'Get started', : '/contact' },
164
},
165
{
166
: 'Enterprise',
167
: 'custom',
168
: '',
169
: 'Critical workloads at scale across multiple regions.',
170
: [
171
{ : 'Unlimited clusters', : true },
172
{ : 'HA + multi-region', : true },
173
{ : 'Full observability + custom dashboards', : true },
174
{ : 'Dedicated Slack + phone', : true },
175
{ : 'GitOps + advanced deployment strategies', : true },
176
{ : 'Multi-cluster + federation', : true },
177
{ : 'Custom platform engineering', : true },
178
],
179
: { : 'Contact us', : '/contact' },
180
},
181
],
182
},
183
'google-apps-management': {
184
: 'per-user',
185
: 8,
186
: 10,
187
: 800,
188
: 'users',
189
: 'Full Google Workspace administration — user lifecycle, security controls, and day-to-day support. One-time setup fee covers migration and initial configuration.',
190
},
191
'office365-management': {
192
: 'per-user',
193
: 9,
194
: 10,
195
: 900,
196
: 'users',
197
: 'Complete Microsoft 365 tenant management — security hardening, user provisioning, Teams/SharePoint, and ongoing support. One-time setup fee covers migration and onboarding.',
198
},
199
'sovereign-productivity-suite': {
200
: 'per-user',
201
: 6,
202
: 25,
203
: 600,
204
: 'mailboxes',
205
: 'Fully self-hosted Zimbra email with OnlyOffice documents and Nextcloud storage. Your data stays on your infrastructure. One-time setup fee covers server provisioning and migration.',
206
},
207
'security-audit': {
208
: 'fixed-project',
209
: 4800,
210
: 'Comprehensive security posture assessment. Delivered in ~4 days.',
211
: [
212
'Vulnerability assessment across infrastructure and applications',
213
'IAM and access control review',
214
'Network segmentation and firewall audit',
215
'Prioritized remediation report with executive summary',
216
],
217
: { : 'Request audit', : '/contact' },
218
},
219
'infrastructure-audit': {
220
: 'fixed-project',
221
: 3200,
222
: 'Architecture and reliability review. Delivered in ~3 days.',
223
: [
224
'Infrastructure architecture review',
225
'Cost and performance analysis',
226
'Scalability and resilience assessment',
227
'Actionable recommendations report',
228
],
229
: { : 'Request audit', : '/contact' },
230
},
231
'cicd-audit': {
232
: 'fixed-project',
233
: 2400,
234
: 'Pipeline efficiency and best-practices review. Delivered in ~2 days.',
235
: [
236
'Build and deployment pipeline analysis',
237
'Test coverage and quality gate review',
238
'Security scanning integration check',
239
'Optimization and tooling recommendations',
240
],
241
: { : 'Request audit', : '/contact' },
242
},
243
'technology-consulting': {
244
: 'hourly',
245
: 120,
246
: 20,
247
: 'Strategic technology guidance from senior engineers. Architecture decisions, technology selection, and roadmap planning.',
248
},
249
'solution-for-saas': {
250
: 'hourly',
251
: 120,
252
: 20,
253
: 'Infrastructure architecture tailored for SaaS products — multi-tenancy, billing integrations, scalability patterns.',
254
},
255
'solution-for-paas': {
256
: 'hourly',
257
: 120,
258
: 20,
259
: 'Platform-as-a-Service architecture design — control planes, tenant isolation, self-service provisioning.',
260
},
261
}
262
263
/** Services that use managed-infra calculator instead of (or in addition to) tiers */
264
export const = new ([
265
'managed-postgresql',
266
'managed-mysql',
267
'managed-mongodb',
268
'managed-redis',
269
'managed-kafka',
270
'managed-opensearch',
271
'managed-runners',
272
'managed-prometheus',
273
'managed-docker-registry',
274
'managed-artifact-repos',
275
'managed-dns',
276
'managed-certificates',
277
])
  • Step 2: Commit
bash
1
git add apps/www/lib/service-pricing-data.ts
2
git commit -m "feat: add centralized per-service pricing data"

Task 3: Create Managed Infra Pricing Logic#

Files:

  • Create: apps/www/lib/managed-infra-pricing.ts

  • Step 1: Create the file

typescript
1
// apps/www/lib/managed-infra-pricing.ts
2
3
export type = 'small' | 'medium' | 'large'
4
5
export interface {
6
: string
7
: string
8
: 'database' | 'ops'
9
: string // e.g. "instance", "runners", "zones"
10
?: {
11
: number | null
12
: number | null
13
: number | null
14
?: string // e.g. "HA Cluster" instead of "Large"
15
}
16
// For per-unit services (runners, DNS zones, certs)
17
?: {
18
: number
19
: string // e.g. "per runner/mo"
20
: number
21
: number
22
: number[] // breakpoint quantities
23
: number[] // price at each breakpoint
24
}
25
?: number // % uplift for HA, or null if HA is included in large
26
}
27
28
export const : [] = [
29
{
30
: 'managed-postgresql',
31
: 'PostgreSQL',
32
: 'database',
33
: 'instance',
34
: { : 400, : 800, : 1600, : 'Large (HA)' },
35
},
36
{
37
: 'managed-mysql',
38
: 'MySQL',
39
: 'database',
40
: 'instance',
41
: { : 350, : 700, : 1400, : 'Large (HA)' },
42
},
43
{
44
: 'managed-mongodb',
45
: 'MongoDB',
46
: 'database',
47
: 'replica set',
48
: { : 600, : 1200, : 2400, : 'Sharded Cluster' },
49
},
50
{
51
: 'managed-redis',
52
: 'Redis',
53
: 'database',
54
: 'instance',
55
: { : 200, : 350, : 600, : 'Cluster Mode' },
56
},
57
{
58
: 'managed-kafka',
59
: 'Kafka',
60
: 'database',
61
: 'cluster',
62
: { : 800, : 1600, : null, : 'Enterprise' },
63
},
64
{
65
: 'managed-opensearch',
66
: 'OpenSearch',
67
: 'database',
68
: 'cluster',
69
: { : 500, : 1000, : 2000, : 'Multi-node' },
70
},
71
{
72
: 'managed-runners',
73
: 'Self-Hosted Runners',
74
: 'ops',
75
: 'runners',
76
: {
77
: 50,
78
: 'per runner/mo',
79
: 5,
80
: 250,
81
: [5, 15, 30, 50],
82
: [250, 650, 1200, 1900],
83
},
84
},
85
{
86
: 'managed-prometheus',
87
: 'Prometheus',
88
: 'ops',
89
: 'setup',
90
: { : 400, : null, : 700, : 'HA Setup' },
91
},
92
{
93
: 'managed-docker-registry',
94
: 'Artifactory',
95
: 'ops',
96
: 'repository service',
97
: { : 300, : null, : null },
98
},
99
{
100
: 'managed-artifact-repos',
101
: 'Artifact Repos',
102
: 'ops',
103
: 'repository',
104
: { : 400, : null, : null },
105
},
106
{
107
: 'managed-dns',
108
: 'DNS as a Service',
109
: 'ops',
110
: 'zones',
111
: {
112
: 20,
113
: 'per zone/mo',
114
: 1,
115
: 200,
116
: [10, 50],
117
: [200, 400],
118
},
119
},
120
{
121
: 'managed-certificates',
122
: 'Certificate Management',
123
: 'ops',
124
: 'certificates',
125
: {
126
: 8,
127
: 'per cert/mo',
128
: 1,
129
: 150,
130
: [20, 100],
131
: [150, 300],
132
},
133
},
134
]
135
136
export function (: string): | undefined {
137
return .(() => . === )
138
}
139
140
export function (: , : , ?: number): number | null {
141
if (.) {
142
const = .[]
143
return // null means "Custom"
144
}
145
if (. && !== ) {
146
const { , , } = .
147
for (let = . - 1; >= 0; --) {
148
if ( >= []) return []
149
}
150
return
151
}
152
return null
153
}
154
155
export function (: number | null): string {
156
if ( === null) return 'Custom'
157
return new .('en-US', {
158
: 'currency',
159
: 'USD',
160
: 0,
161
: 0,
162
}).()
163
}
  • Step 2: Commit
bash
1
git add apps/www/lib/managed-infra-pricing.ts
2
git commit -m "feat: add managed infra pricing logic"

Task 4: Create ServicePricingSection Component#

Files:

  • Create: apps/www/components/Pricing/ServicePricingSection.tsx

This component reads from service-pricing-data.ts and renders the correct pricing UI based on the model type.

  • Step 1: Create the component
tsx
1
// apps/www/components/Pricing/ServicePricingSection.tsx
2
'use client'
3
4
import Link from 'next/link'
5
import { Check, Minus, Clock, Users, FolderCheck } from 'lucide-react'
6
import { Button, cn } from 'ui'
7
import { servicePricing } from '~/lib/service-pricing-data'
8
import type {
9
HourlyPricing,
10
MonthlyTierPricing,
11
PerUserPricing,
12
FixedProjectPricing,
13
} from '~/lib/service-pricing-data'
14
15
// ── Hourly ───────────────────────────────────────────────────────────────────
16
function HourlySection({ pricing }: { pricing: HourlyPricing }) {
17
const retainer = pricing.monthlyRetainer ?? (pricing.rate * (pricing.minHours ?? 1))
18
return (
19
<div className="rounded-xl border border-default bg-surface-75 p-8 max-w-2xl">
20
<div className="flex items-center gap-2 text-foreground-lighter text-sm mb-4">
21
<Clock className="w-4 h-4" />
22
<span>Hourly rate</span>
23
</div>
24
<div className="flex items-baseline gap-2 mb-2">
25
<span className="text-5xl font-medium font-mono text-foreground">${pricing.rate}</span>
26
<span className="text-foreground-lighter">/hr</span>
27
</div>
28
{pricing.minHours && (
29
<p className="text-sm text-foreground-lighter mb-1">
30
Minimum engagement: {pricing.minHours} hours
31
{pricing.monthlyRetainer ? ` (${new Intl.NumberFormat('en-US',{style:'currency',currency:'USD',minimumFractionDigits:0}).format(retainer)}/mo retainer)` : ''}
32
</p>
33
)}
34
<p className="text-foreground-lighter text-sm mt-3 mb-6">{pricing.description}</p>
35
<div className="flex gap-3">
36
<Button asChild size="medium" type="primary">
37
<Link href="/contact">Get a quote</Link>
38
</Button>
39
<Button asChild size="medium" type="default">
40
<Link href="/contact">Talk to us</Link>
41
</Button>
42
</div>
43
</div>
44
)
45
}
46
47
// ── Monthly Tiers ─────────────────────────────────────────────────────────────
48
function MonthlyTiersSection({ pricing }: { pricing: MonthlyTierPricing }) {
49
return (
50
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 lg:gap-6">
51
{pricing.tiers.map((tier) => {
52
const priceDisplay = tier.price === 'custom' ? 'Custom' : `$${tier.price.toLocaleString()}`
53
return (
54
<div
55
key={tier.name}
56
className={cn(
57
'flex flex-col border rounded-xl bg-surface-75 p-6 lg:p-8 relative',
58
tier.highlight && 'border-brand ring-1 ring-brand'
59
)}
60
>
61
{tier.highlight && (
62
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-brand text-background text-xs font-medium px-3 py-1 rounded-full">
63
Most popular
64
</div>
65
)}
66
<h4 className="text-foreground text-lg font-medium font-mono uppercase">{tier.name}</h4>
67
<div className="mt-3 flex items-baseline gap-1">
68
<span className="text-foreground text-4xl font-medium font-mono">{priceDisplay}</span>
69
{tier.priceUnit && (
70
<span className="text-foreground-lighter text-sm">{tier.priceUnit}</span>
71
)}
72
</div>
73
<p className="text-foreground-lighter text-sm mt-3 min-h-[3rem]">{tier.description}</p>
74
<Button asChild size="medium" type={tier.highlight ? 'primary' : 'default'} block className="mt-6">
75
<Link href={tier.cta.href}>{tier.cta.label}</Link>
76
</Button>
77
<ul className="mt-6 space-y-3 flex-1">
78
{tier.features.map((feature) => (
79
<li key={feature.label} className="flex items-start gap-2.5 text-sm">
80
{feature.included ? (
81
<Check className="w-4 h-4 text-brand shrink-0 mt-0.5" strokeWidth={2.5} />
82
) : (
83
<Minus className="w-4 h-4 text-foreground-muted shrink-0 mt-0.5" strokeWidth={2} />
84
)}
85
<span className={cn(feature.included ? 'text-foreground' : 'text-foreground-muted')}>
86
{feature.label}
87
</span>
88
</li>
89
))}
90
</ul>
91
</div>
92
)
93
})}
94
</div>
95
)
96
}
97
98
// ── Per User ──────────────────────────────────────────────────────────────────
99
function PerUserSection({ pricing }: { pricing: PerUserPricing }) {
100
const minMonthly = pricing.ratePerUser * pricing.minUsers
101
const fmt = (n: number) =>
102
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0 }).format(n)
103
return (
104
<div className="rounded-xl border border-default bg-surface-75 p-8 max-w-2xl">
105
<div className="flex items-center gap-2 text-foreground-lighter text-sm mb-4">
106
<Users className="w-4 h-4" />
107
<span>Per {pricing.userLabel}</span>
108
</div>
109
<div className="flex items-baseline gap-2 mb-2">
110
<span className="text-5xl font-medium font-mono text-foreground">${pricing.ratePerUser}</span>
111
<span className="text-foreground-lighter">/{pricing.userLabel.replace(/s$/, '')}/mo</span>
112
</div>
113
<p className="text-sm text-foreground-lighter mb-1">
114
Minimum {pricing.minUsers} {pricing.userLabel} — from {fmt(minMonthly)}/mo
115
</p>
116
<p className="text-sm text-foreground-lighter mb-1">
117
One-time setup fee: {fmt(pricing.setupFee)}
118
</p>
119
<p className="text-foreground-lighter text-sm mt-3 mb-6">{pricing.description}</p>
120
<div className="flex gap-3">
121
<Button asChild size="medium" type="primary">
122
<Link href="/contact">Get started</Link>
123
</Button>
124
<Button asChild size="medium" type="default">
125
<Link href="/contact">Request a quote</Link>
126
</Button>
127
</div>
128
</div>
129
)
130
}
131
132
// ── Fixed Project ─────────────────────────────────────────────────────────────
133
function FixedProjectSection({ pricing }: { pricing: FixedProjectPricing }) {
134
const fmt = (n: number) =>
135
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0 }).format(n)
136
return (
137
<div className="rounded-xl border border-default bg-surface-75 p-8 max-w-2xl">
138
<div className="flex items-center gap-2 text-foreground-lighter text-sm mb-4">
139
<FolderCheck className="w-4 h-4" />
140
<span>Fixed project price</span>
141
</div>
142
<div className="flex items-baseline gap-2 mb-3">
143
<span className="text-5xl font-medium font-mono text-foreground">{fmt(pricing.price)}</span>
144
</div>
145
<p className="text-foreground-lighter text-sm mb-4">{pricing.description}</p>
146
<ul className="space-y-2 mb-6">
147
{pricing.includes.map((item) => (
148
<li key={item} className="flex items-start gap-2.5 text-sm">
149
<Check className="w-4 h-4 text-brand shrink-0 mt-0.5" strokeWidth={2.5} />
150
<span className="text-foreground">{item}</span>
151
</li>
152
))}
153
</ul>
154
<Button asChild size="medium" type="primary">
155
<Link href={pricing.cta.href}>{pricing.cta.label}</Link>
156
</Button>
157
</div>
158
)
159
}
160
161
// ── Main export ───────────────────────────────────────────────────────────────
162
export default function ServicePricingSection({ slug }: { slug: string }) {
163
const pricing = servicePricing[slug]
164
if (!pricing) return null
165
166
return (
167
<section className="space-y-6">
168
<div>
169
<h2 className="text-foreground text-2xl sm:text-3xl font-medium tracking-tight mb-2">Pricing</h2>
170
<p className="text-foreground-lighter text-sm">
171
No long-term contracts.{' '}
172
<Link href="/contact" className="text-brand hover:underline">
173
Contact us
174
</Link>{' '}
175
for custom arrangements.
176
</p>
177
</div>
178
{pricing.model === 'hourly' && <HourlySection pricing={pricing} />}
179
{pricing.model === 'monthly-tiers' && <MonthlyTiersSection pricing={pricing} />}
180
{pricing.model === 'per-user' && <PerUserSection pricing={pricing} />}
181
{pricing.model === 'fixed-project' && <FixedProjectSection pricing={pricing} />}
182
</section>
183
)
184
}
  • Step 2: Commit
bash
1
git add apps/www/components/Pricing/ServicePricingSection.tsx
2
git commit -m "feat: add ServicePricingSection component for service pages"

Task 5: Create Managed Infra Calculator Component#

Files:

  • Create: apps/www/components/Pricing/ManagedInfraCalculator.tsx

  • Step 1: Create the component

tsx
1
// apps/www/components/Pricing/ManagedInfraCalculator.tsx
2
'use client'
3
4
import { useState } from 'react'
5
import { Button, cn } from 'ui'
6
import Link from 'next/link'
7
import {
8
managedInfraServices,
9
getServiceById,
10
estimateMonthlyPrice,
11
formatPrice,
12
type InfraSize,
13
type ManagedInfraService,
14
} from '~/lib/managed-infra-pricing'
15
16
interface ServiceRow {
17
id: string
18
enabled: boolean
19
size: InfraSize
20
units: number
21
}
22
23
function createDefaultRows(): ServiceRow[] {
24
return managedInfraServices.map((s) => ({
25
id: s.id,
26
enabled: false,
27
size: 'small',
28
units: s.perUnit?.minUnits ?? 1,
29
}))
30
}
31
32
function calcServicePrice(service: ManagedInfraService, row: ServiceRow): number | null {
33
if (service.sizes) {
34
return estimateMonthlyPrice(service, row.size)
35
}
36
if (service.perUnit) {
37
return estimateMonthlyPrice(service, row.size, row.units)
38
}
39
return null
40
}
41
42
export default function ManagedInfraCalculator({ highlightSlug }: { highlightSlug?: string }) {
43
const [rows, setRows] = useState<ServiceRow[]>(createDefaultRows)
44
45
const total = rows.reduce((sum, row) => {
46
if (!row.enabled) return sum
47
const svc = getServiceById(row.id)
48
if (!svc) return sum
49
return sum + (calcServicePrice(svc, row) ?? 0)
50
}, 0)
51
52
const toggleRow = (id: string) =>
53
setRows((prev) => prev.map((r) => (r.id === id ? { ...r, enabled: !r.enabled } : r)))
54
55
const setSize = (id: string, size: InfraSize) =>
56
setRows((prev) => prev.map((r) => (r.id === id ? { ...r, size } : r)))
57
58
const setUnits = (id: string, units: number) =>
59
setRows((prev) => prev.map((r) => (r.id === id ? { ...r, units } : r)))
60
61
const databases = managedInfraServices.filter((s) => s.category === 'database')
62
const ops = managedInfraServices.filter((s) => s.category === 'ops')
63
64
const renderCategory = (services: ManagedInfraService[], label: string) => (
65
<div>
66
<h3 className="text-foreground-lighter text-xs font-medium uppercase tracking-widest mb-3">{label}</h3>
67
<div className="space-y-2">
68
{services.map((svc) => {
69
const row = rows.find((r) => r.id === svc.id)!
70
const price = row.enabled ? calcServicePrice(svc, row) : null
71
const isHighlighted = svc.id === highlightSlug
72
return (
73
<div
74
key={svc.id}
75
className={cn(
76
'flex flex-col sm:flex-row sm:items-center gap-3 rounded-lg border p-4 transition-colors',
77
row.enabled ? 'border-brand bg-brand/5' : 'border-default bg-surface-75',
78
isHighlighted && !row.enabled && 'border-brand/40'
79
)}
80
>
81
{/* Toggle + label */}
82
<label className="flex items-center gap-3 cursor-pointer flex-1 min-w-0">
83
<input
84
type="checkbox"
85
checked={row.enabled}
86
onChange={() => toggleRow(svc.id)}
87
className="accent-brand w-4 h-4 shrink-0"
88
/>
89
<span className="text-foreground text-sm font-medium truncate">{svc.label}</span>
90
</label>
91
92
{/* Size selector (for services with sizes) */}
93
{svc.sizes && row.enabled && (
94
<div className="flex gap-1 shrink-0">
95
{(['small', 'medium', 'large'] as InfraSize[]).map((s) => {
96
const p = svc.sizes![s]
97
if (p === undefined) return null
98
const name = s === 'large' ? (svc.sizes!.largeName ?? 'Large') : s.charAt(0).toUpperCase() + s.slice(1)
99
return (
100
<button
101
key={s}
102
onClick={() => setSize(svc.id, s)}
103
disabled={p === null}
104
className={cn(
105
'px-2.5 py-1 text-xs rounded border font-medium transition-colors',
106
row.size === s
107
? 'border-brand bg-brand text-background'
108
: 'border-default text-foreground-lighter hover:border-foreground-light',
109
p === null && 'opacity-40 cursor-not-allowed'
110
)}
111
>
112
{name}
113
</button>
114
)
115
})}
116
</div>
117
)}
118
119
{/* Units selector (for per-unit services) */}
120
{svc.perUnit && row.enabled && (
121
<div className="flex items-center gap-2 shrink-0">
122
<input
123
type="number"
124
min={svc.perUnit.minUnits}
125
step={1}
126
value={row.units}
127
onChange={(e) => setUnits(svc.id, Math.max(svc.perUnit!.minUnits, Number(e.target.value)))}
128
className="w-20 px-2 py-1 text-sm border border-default rounded bg-background text-foreground"
129
/>
130
<span className="text-xs text-foreground-lighter whitespace-nowrap">{svc.perUnit.label}</span>
131
</div>
132
)}
133
134
{/* Price display */}
135
<div className="text-right shrink-0 w-24">
136
{row.enabled ? (
137
<span className="text-foreground font-medium font-mono text-sm">
138
{price === null ? 'Custom' : formatPrice(price)}<span className="text-foreground-lighter font-normal">/mo</span>
139
</span>
140
) : (
141
<span className="text-foreground-muted text-xs">
142
{svc.sizes?.small ? `from ${formatPrice(svc.sizes.small)}/mo` : svc.perUnit ? `from ${formatPrice(svc.perUnit.minPrice)}/mo` : ''}
143
</span>
144
)}
145
</div>
146
</div>
147
)
148
})}
149
</div>
150
</div>
151
)
152
153
return (
154
<div className="space-y-6">
155
<div>
156
<h2 className="text-foreground text-2xl sm:text-3xl font-medium tracking-tight mb-2">
157
Pricing calculator
158
</h2>
159
<p className="text-foreground-lighter text-sm">
160
Select the services you need to estimate your monthly cost.
161
</p>
162
</div>
163
164
<div className="space-y-6">
165
{renderCategory(databases, 'Databases')}
166
{renderCategory(ops, 'Observability & Ops')}
167
</div>
168
169
{/* Total */}
170
<div className="rounded-xl border border-default bg-alternative p-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
171
<div>
172
<p className="text-foreground-lighter text-sm">Estimated monthly total</p>
173
<p className="text-foreground text-3xl font-medium font-mono mt-1">
174
{formatPrice(total)}
175
<span className="text-foreground-lighter text-base font-normal">/mo</span>
176
</p>
177
<p className="text-foreground-muted text-xs mt-1">
178
Does not include server infrastructure costs (compute, storage, egress).
179
</p>
180
</div>
181
<div className="flex gap-3 shrink-0">
182
<Button asChild size="medium" type="primary">
183
<Link href="/contact">Get an exact quote</Link>
184
</Button>
185
</div>
186
</div>
187
</div>
188
)
189
}
  • Step 2: Commit
bash
1
git add apps/www/components/Pricing/ManagedInfraCalculator.tsx
2
git commit -m "feat: add ManagedInfraCalculator component"

Task 6: Inject Pricing into Service Page Template#

Files:

  • Modify: apps/www/_pages/services/[slug].tsx

  • Step 1: Read the current file (already done during planning)

  • Step 2: Add imports

Add at top of _pages/services/[slug].tsx:

typescript
1
import dynamic from 'next/dynamic'
2
import { MANAGED_INFRA_SLUGS } from '~/lib/service-pricing-data'
3
4
const ServicePricingSection = dynamic(() => import('~/components/Pricing/ServicePricingSection'))
5
const ManagedInfraCalculator = dynamic(() => import('~/components/Pricing/ManagedInfraCalculator'))
  • Step 3: Inject pricing section after ComparisonTable

In the JSX render, after the <ComparisonTable> section and before <ServiceTestimonials>, add (with id="pricing" anchor for deep-linking from the hub page):

tsx
1
{/* Pricing section — rendered for services with pricing data */}
2
<div id="pricing">
3
{!MANAGED_INFRA_SLUGS.has(slug) && (
4
<SectionContainer>
5
<ServicePricingSection slug={slug} />
6
</SectionContainer>
7
)}
8
9
{/* Managed infra calculator — rendered for all managed infra services */}
10
{MANAGED_INFRA_SLUGS.has(slug) && (
11
<SectionContainer>
12
<ManagedInfraCalculator highlightSlug={slug} />
13
</SectionContainer>
14
)}
15
</div>
  • Step 4: Commit
bash
1
git add apps/www/_pages/services/[slug].tsx
2
git commit -m "feat: inject pricing section into service page template"

Task 7: Convert Global Pricing Page to Hub#

Files:

  • Modify: apps/www/app/pricing/PricingContent.tsx

The /pricing page becomes a lightweight hub that lists services with their pricing model and links to each service's page for full details.

  • Step 1: Rewrite PricingContent.tsx

Replace the entire content with:

tsx
1
'use client'
2
3
import Link from 'next/link'
4
import { Clock, LayoutGrid, Users, FolderCheck, Calculator } from 'lucide-react'
5
import { Button } from 'ui'
6
import DefaultLayout from '~/components/Layouts/Default'
7
import SectionContainer from '~/components/Layouts/SectionContainer'
8
9
const sections = [
10
{
11
category: 'DevOps & SRE',
12
icon: Clock,
13
description: 'Billed hourly with monthly retainer options.',
14
services: [
15
{ name: 'DevOps as a Service', slug: 'devops-as-a-service', rate: '$120/hr', min: '40 hr/mo minimum' },
16
{ name: 'SRE as a Service', slug: 'sre-as-a-service', rate: '$130/hr', min: '40 hr/mo minimum' },
17
{ name: 'DevOps Emergency', slug: 'emergency', rate: '$200/hr', min: '4 hr minimum, 24/7' },
18
{ name: 'DevOps Trainings', slug: 'devops-trainings', rate: '$150/hr', min: 'per trainer session' },
19
],
20
},
21
{
22
category: 'Cloud & Kubernetes',
23
icon: LayoutGrid,
24
description: 'Monthly managed service tiers.',
25
services: [
26
{ name: 'Cloud Account Management', slug: 'cloud-account-management', rate: 'from $1,500/mo', min: '' },
27
{ name: 'Managed Kubernetes', slug: 'managed-kubernetes', rate: 'from $3,000/mo', min: '' },
28
{ name: 'Managed Kubernetes (K3s option)', slug: 'managed-kubernetes', rate: 'from $3,000/mo', min: '' },
29
],
30
},
31
{
32
category: 'Managed Infrastructure',
33
icon: Calculator,
34
description: 'Per-service monthly pricing. Use the calculator on each service page.',
35
services: [
36
{ name: 'Managed PostgreSQL', slug: 'managed-postgresql', rate: 'from $400/mo', min: '' },
37
{ name: 'Managed MySQL', slug: 'managed-mysql', rate: 'from $350/mo', min: '' },
38
{ name: 'Managed MongoDB', slug: 'managed-mongodb', rate: 'from $600/mo', min: '' },
39
{ name: 'Managed Redis', slug: 'managed-redis', rate: 'from $200/mo', min: '' },
40
{ name: 'Managed Kafka', slug: 'managed-kafka', rate: 'from $800/mo', min: '' },
41
{ name: 'Managed OpenSearch', slug: 'managed-opensearch', rate: 'from $500/mo', min: '' },
42
{ name: 'Managed Runners', slug: 'managed-runners', rate: 'from $250/mo', min: '5 runners minimum' },
43
{ name: 'Managed Prometheus', slug: 'managed-prometheus', rate: 'from $400/mo', min: '' },
44
{ name: 'Managed Artifactory', slug: 'managed-docker-registry', rate: '$300/mo', min: '' },
45
{ name: 'Managed Artifact Repos', slug: 'managed-artifact-repos', rate: '$400/mo', min: '' },
46
{ name: 'DNS as a Service', slug: 'managed-dns', rate: 'from $200/mo', min: '' },
47
{ name: 'Certificate Management', slug: 'managed-certificates', rate: 'from $150/mo', min: '' },
48
],
49
},
50
{
51
category: 'Productivity Suites',
52
icon: Users,
53
description: 'Per-user/mailbox monthly pricing with one-time setup.',
54
services: [
55
{ name: 'Google Workspace Management', slug: 'google-apps-management', rate: '$8/user/mo', min: '10 users minimum' },
56
{ name: 'Microsoft 365 Management', slug: 'office365-management', rate: '$9/user/mo', min: '10 users minimum' },
57
{ name: 'Zimbra (Self-Hosted)', slug: 'sovereign-productivity-suite', rate: '$6/mailbox/mo', min: '25 mailboxes minimum' },
58
],
59
},
60
{
61
category: 'Consulting & Audits',
62
icon: FolderCheck,
63
description: 'Fixed-price project engagements and hourly advisory.',
64
services: [
65
{ name: 'Security Audit', slug: 'security-audit', rate: '$4,800', min: 'fixed project' },
66
{ name: 'Infrastructure Audit', slug: 'infrastructure-audit', rate: '$3,200', min: 'fixed project' },
67
{ name: 'CI/CD Audit', slug: 'cicd-audit', rate: '$2,400', min: 'fixed project' },
68
{ name: 'Technology Consulting', slug: 'technology-consulting', rate: '$120/hr', min: '20 hr minimum' },
69
{ name: 'Solution for SaaS', slug: 'solution-for-saas', rate: '$120/hr', min: '20 hr minimum' },
70
{ name: 'Building PaaS', slug: 'solution-for-paas', rate: '$120/hr', min: '20 hr minimum' },
71
],
72
},
73
]
74
75
export default function PricingContent() {
76
return (
77
<DefaultLayout>
78
<div className="relative z-10 pt-8 pb-4 xl:py-16">
79
<div className="mx-auto max-w-7xl px-8 text-center sm:px-6 lg:px-8">
80
<h1 className="text-foreground text-3xl sm:text-4xl md:text-5xl font-medium tracking-tight">
81
Transparent pricing, no surprises
82
</h1>
83
<p className="text-foreground-lighter text-lg leading-relaxed max-w-2xl mx-auto mt-4">
84
Fixed hourly rates for DevOps work. Monthly managed pricing for infrastructure.
85
No long-term contracts required.
86
</p>
87
<div className="flex flex-wrap items-center justify-center gap-3 pt-4">
88
<Button asChild size="medium" type="primary">
89
<Link href="/contact">Get a custom quote</Link>
90
</Button>
91
<Button asChild size="medium" type="default">
92
<Link href="/contact">Talk to our team</Link>
93
</Button>
94
</div>
95
</div>
96
</div>
97
98
<SectionContainer>
99
<div className="space-y-16">
100
{sections.map((section) => {
101
const Icon = section.icon
102
return (
103
<div key={section.category}>
104
<div className="flex items-center gap-3 mb-2">
105
<Icon className="w-5 h-5 text-brand" />
106
<h2 className="text-foreground text-xl font-medium">{section.category}</h2>
107
</div>
108
<p className="text-foreground-lighter text-sm mb-6">{section.description}</p>
109
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
110
{section.services.map((svc) => (
111
<Link
112
key={svc.slug}
113
href={`/services/${svc.slug}#pricing`}
114
className="group flex flex-col border border-default rounded-lg p-4 bg-surface-75 hover:border-brand transition-colors"
115
>
116
<span className="text-foreground text-sm font-medium group-hover:text-brand transition-colors">
117
{svc.name}
118
</span>
119
<span className="text-brand font-mono text-sm mt-1">{svc.rate}</span>
120
{svc.min && (
121
<span className="text-foreground-muted text-xs mt-0.5">{svc.min}</span>
122
)}
123
</Link>
124
))}
125
</div>
126
</div>
127
)
128
})}
129
</div>
130
</SectionContainer>
131
132
<div className="border-t py-16 text-center">
133
<div className="max-w-lg mx-auto px-6">
134
<h2 className="text-foreground text-2xl font-medium mb-3">Need multiple services?</h2>
135
<p className="text-foreground-lighter text-sm mb-6">
136
Most clients use 2-4 services together. Contact us for bundled pricing.
137
</p>
138
<div className="flex flex-wrap items-center justify-center gap-3">
139
<Button asChild size="medium" type="primary">
140
<Link href="/contact">Get a bundled quote</Link>
141
</Button>
142
</div>
143
</div>
144
</div>
145
</DefaultLayout>
146
)
147
}
  • Step 2: Commit
bash
1
git add apps/www/app/pricing/PricingContent.tsx apps/www/_pages/services/[slug].tsx
2
git commit -m "feat: convert global pricing to hub page, add pricing anchors on service pages"

Task 8: Update Pricing Page Metadata#

Files:

  • Modify: apps/www/app/pricing/page.tsx

  • Step 1: Update metadata description

Change the description in the page metadata to:

1
"Fixed hourly rates for DevOps services. Monthly managed pricing for databases, Kubernetes, and cloud. Transparent pricing on every service page."
  • Step 2: Commit
bash
1
git add apps/www/app/pricing/page.tsx
2
git commit -m "chore: update pricing page metadata"

Verification#

  • Run cd apps/www && pnpm dev — app starts without errors
  • Visit /pricing — hub page shows all service categories with rates
  • Visit /services/devops-as-a-service#pricing — hourly pricing section renders
  • Visit /services/cloud-account-management#pricing — 3-tier monthly pricing renders
  • Visit /services/managed-postgresql#pricing — infra calculator renders with PostgreSQL highlighted
  • Visit /services/managed-runners#pricing — calculator shows runners with unit selector
  • Visit /services/google-apps-management#pricing — per-user pricing renders
  • Visit /services/sovereign-productivity-suite#pricing — Zimbra per-mailbox pricing renders
  • Visit /services/security-audit#pricing — fixed project pricing renders
  • Main nav has no "Pricing" top-level link
  • No dropdown menus show a "Pricing" entry
  • Footer services links do not include "Pricing"
  • Calculator total updates reactively when toggling services