Skip to content

SEO refresh: Deploy MERN Stack on DigitalOcean with Pulumi#18742

Draft
CamSoper wants to merge 5 commits intomasterfrom
claude/seo-refresh-mern-blog-okmI2
Draft

SEO refresh: Deploy MERN Stack on DigitalOcean with Pulumi#18742
CamSoper wants to merge 5 commits intomasterfrom
claude/seo-refresh-mern-blog-okmI2

Conversation

@CamSoper
Copy link
Copy Markdown
Contributor

Conditional SEO refresh for the fullstack-pulumi-mern-stack-digitalocean blog post per pulumi/marketing#1683.

The post peaked at 1,133 organic PVs in March 2022 and dropped 99.1% to 10 by March 2026 (T12M = 124). The classification is conditional refresh — keep it lean, so this PR restructures the post for answer-first SEO without rewriting the working code walkthrough.

Summary

  • Answer-first intro: Replaced the long anecdotal opening with a 3-sentence direct answer to "how do you deploy MERN on DigitalOcean with Pulumi?"
  • TL;DR section at the top with what you'll build, write, need, why, and how long it takes.
  • H2s rephrased as user questions: "Charting a course" → "How do you map MERN tiers to App Platform components?", "Creating the project" → "How do you create the Pulumi project?", etc.
  • HowTo schema (JSON-LD): Inline <script type="application/ld+json"> block covering the six deployment steps, with tool entries for the prerequisites. Sits alongside the auto-generated BlogPosting schema rather than replacing it.
  • Comparison table: DigitalOcean vs. Render vs. Railway vs. Fly.io for MERN deployments — the only one of the four with first-party Managed MongoDB and a mature first-party Pulumi provider.
  • 2026 DigitalOcean services: Updated Managed MongoDB engine version from 57 (TS and Python). Re-verified DO product names and links (App Platform, Managed Databases).
  • Code re-verification: Caught and fixed the local MongoDB port typo (2710727017); confirmed remaining CLI commands and Pulumi resources still match the current @pulumi/digitalocean API surface.
  • Internal links from current Pulumi full-stack content: Added cross-links to Deploying a PERN stack application to AWS, Getting started on DigitalOcean with Pulumi, and Pulumi ESC. Promoted /docs/install/ → canonical /docs/get-started/download-install/.
  • "Last updated" bump to 2026-04-30 plus a Changelog section at the bottom.

Explicitly not included

Per the issue: no FAQPage schema and no FAQ section. Labelling a blog post as a FAQPage violates Google's structured-data guidelines.

Test plan

  • make build produces the post without Hugo template errors (the inline <script type="application/ld+json"> relies on markup.goldmark.renderer.unsafe: true, which is already set in config/_default/config.yml).
  • Rendered page validates clean in Google's Rich Results Test for HowTo.
  • Both the existing BlogPosting schema (from the auto collector) and the new HowTo JSON-LD appear in the rendered HTML.
  • Comparison table renders correctly.
  • #setting-up anchor still works (preserved on the renamed H2).

Refs pulumi/marketing#1683.

https://claude.ai/code/session_01BmfMjLUcoNJfhaiWoZhNLj


Generated by Claude Code

claude and others added 2 commits April 30, 2026 23:44
Conditional refresh per pulumi/marketing#1683 (peaked at 1,133 organic
PVs in March 2022, T12M now 124). Restructures for answer-first SEO
without rewriting the working code walkthrough.

- Answer-first intro and TL;DR up top
- H2s rephrased as user questions
- Inline HowTo JSON-LD covering the six deployment steps
- Comparison table: DigitalOcean vs. Render vs. Railway vs. Fly.io for MERN
- Bump Managed MongoDB from version 5 to version 7
- Fix local Mongo port typo (27107 -> 27017)
- Internal links to PERN/AWS, Getting started on DigitalOcean, and Pulumi ESC
- Bump updated date to 2026-04-30 and add changelog

No FAQPage schema or FAQ section per Google structured-data guidance.

Co-Authored-By: Claude <noreply@anthropic.com>
- Correct Railway "Managed MongoDB" table cell to "No (unmanaged template)" per Railway's own docs
- Align TL;DR "Time to deploy" (~15 min) with HowTo schema totalTime PT15M
- Fix missing article: "creating a new Pulumi project"
- Drop redundant "as well" in App Platform GitLab note
- Drop empty qualifier "easily" from DatabaseFirewall recommendation
- Drop empty intensifiers "definitely" and "a little" from closing paragraph

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 30, 2026

Docs Review — PR #18742

Solid SEO refresh with thoughtful structural changes. Most of the post improves notably; a few items worth addressing before merge.

Issues

1. meta_desc exceeds the recommended length (line 5)

The meta description is 203 characters, well over the ~160-char ceiling search engines display. It will be truncated in SERPs and may dilute the keyword density at the front of the snippet.

meta_desc: Deploy a MERN stack app to DigitalOcean App Platform with Pulumi. A step-by-step TypeScript and Python guide that codifies the front end, Express API, and Managed MongoDB cluster as one stack.

That trims to ~195 — still long. A tighter rewrite:

meta_desc: Deploy a MERN stack app to DigitalOcean App Platform with Pulumi. Codify the React front end, Express API, and Managed MongoDB cluster as one repeatable TypeScript or Python program.

That's ~189; further trimming may be worthwhile to land under 160.

2. H2 headings are questions, not answer-first (lines 81, 91, 107, 121, 145, 153, 191, 569, 735)

The repo's review criteria explicitly state: "H2 headings use answer-first phrasing (lead with the answer, not the question)." The PR's pivot to question-form H2s ("Why deploy MERN…?", "How do you map MERN tiers…?") is deliberate per the PR description, but it cuts against that guideline. Question-form H2s can work for AEO when paired with a 1–2 sentence answer immediately below — which most sections here do — so this is more of a guideline-compliance flag than a hard problem. Worth a quick reread to confirm each H2 has a tight answer in its first sentence; lines 191 ("How do you create the Pulumi project?") and 569 ("How do you deploy the app?") could lead more directly with the answer.

3. HowTo JSON-LD will not produce a rich result (lines 27–79)

Heads-up: Google deprecated the HowTo rich result in September 2023 and stopped showing it in search entirely. The schema is still valid markup and harmless, but it won't deliver the SERP benefit the PR description implies. Consider whether the maintenance cost of keeping the inline JSON-LD in sync with the prose is worth it given there's no rich-result payoff. (FWIW: the auto-generated BlogPosting schema continues to do its job.)

4. Anchor link verification — looks correct

The TL;DR's #how-does-digitalocean-compare-to-render-railway-and-flyio-for-mern anchor (line 24) matches Hugo's slugification of the H2 on line 91. Good.

Minor / nits

  • Em-dashes in the intro (line 15): Two em-dashes in two sentences is at the upper edge of the "1–2 per section" guideline. Reads OK but worth noting if you're sweeping for AI-writing patterns elsewhere.
  • Comparison table claims (lines 95–103): Render/Railway/Fly.io capabilities shift quickly. The claim "Railway: No (unmanaged template; Postgres native)" is roughly current, but worth re-verifying close to publish in case any of those vendors have since shipped first-party Managed MongoDB.
  • HowTo schema step 4 (line 63): "listening on port 8000 at /api" — the prose walkthrough later sets BACKEND_ROUTE_PREFIX defaulting to /api (line 145) but doesn't lock port 8000 in this PR. Minor; the schema is informational.
  • Changelog section (lines 749–753): Nice addition. Consider whether you want this becoming a pattern across other refreshed posts — if so, may be worth a shortcode.

What's working well

  • Code accuracy fix on local MongoDB port (27107 → 27017, line 169) — good catch.
  • Managed MongoDB engine bump 5 → 7 in both TypeScript and Python samples (lines 284, 307).
  • /docs/install/ → canonical /docs/get-started/download-install/ (lines 23, 112).
  • {#setting-up} anchor preserved on the renamed H2 (line 107) — keeps inbound links working.
  • All four added internal links resolve: /blog/deploying-a-pern-stack-application-to-aws/, /blog/getting-started-on-digitalocean-with-pulumi/, /docs/esc/, /docs/get-started/download-install/.

Publishing-readiness checklist

  • meta_image set (meta.png)
  • <!--more--> break present (line 17)
  • Author profile referenced (christian-nunciato)
  • All links resolve
  • Code examples have language specifiers
  • Title ≤60 chars
  • meta_desc ≤160 chars — currently 203
  • social: block — not present (this is an updated post, not a new one, so likely intentional)

Mention @claude if you'd like another pass after revisions or want help tightening the meta description.

@CamSoper
Copy link
Copy Markdown
Contributor Author

CamSoper commented May 1, 2026

@claude implement your suggestion for item 1.

@pulumi-bot
Copy link
Copy Markdown
Collaborator

pulumi-bot commented May 1, 2026

Claude finished @CamSoper's task in 1m 34s —— View job


meta_desc updated

  • Read the current file to find the meta_desc
  • Update meta_desc to the tighter version
  • Commit and push the change

Trimmed meta_desc from 204 → 182 characters (commit abafb05):

Before:

Deploy a MERN stack app to DigitalOcean App Platform with Pulumi. A step-by-step TypeScript and Python guide that codifies the front end, Express API, and Managed MongoDB cluster as one repeatable stack.

After:

Deploy a MERN stack app to DigitalOcean App Platform with Pulumi. Codify the React front end, Express API, and Managed MongoDB cluster as one repeatable TypeScript or Python program.

This is the tighter rewrite I suggested in item 1. It's 182 characters — improved over the original 204, though still slightly above the ~160-char ideal SERP display window. If you'd like it trimmed to under 160, let me know and I can take another pass (e.g., dropping "the React front end" → "React, Express API, and Managed MongoDB" saves another ~20 chars).
| Branch

…ncation

Co-authored-by: Cam Soper <CamSoper@users.noreply.github.com>
@CamSoper
Copy link
Copy Markdown
Contributor Author

CamSoper commented May 1, 2026

@claude fix the linting errors, please

@pulumi-bot
Copy link
Copy Markdown
Collaborator

pulumi-bot commented May 1, 2026

Claude finished @CamSoper's task in 2m 8s —— View job


Linting error fixed

  • Gather context
  • Identified linting error: meta_desc was 182 characters, exceeding the 160-character limit enforced by scripts/lint/lint-markdown.js
  • Fixed meta_desc to 159 characters (commit 0d8abbd)
  • Pushed to branch

Before (182 chars):

Deploy a MERN stack app to DigitalOcean App Platform with Pulumi. Codify the React front end, Express API, and Managed MongoDB cluster as one repeatable TypeScript or Python program.

After (159 chars):

Deploy a MERN stack to DigitalOcean App Platform with Pulumi. Codify the React front end, Express API, and Managed MongoDB as one TypeScript or Python program.

Changes: dropped "app " (4 chars), "cluster" (7 chars), and "repeatable " (11 chars) from the description. No other linting issues found — markdown files are excluded from Prettier via .prettierignore, and the title (53 chars) and meta image are both within limits.
| Branch

Co-authored-by: Cam Soper <CamSoper@users.noreply.github.com>
@CamSoper CamSoper enabled auto-merge (squash) May 1, 2026 18:36
@cnunciato cnunciato requested review from cnunciato and Copilot May 2, 2026 15:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

SEO-focused refresh of the fullstack-pulumi-mern-stack-digitalocean blog post to provide an answer-first structure while keeping the existing walkthrough largely intact.

Changes:

  • Replaced the opening with an answer-first intro and added a TL;DR section near the top.
  • Added inline HowTo JSON-LD structured data and a provider comparison table (DigitalOcean vs Render/Railway/Fly.io).
  • Updated a few technical details/links (e.g., MongoDB version 5 → 7 in examples, fixed local MongoDB port, updated Pulumi install link), and added a changelog.

Comment on lines +22 to +24
- **What you'll write:** A single Pulumi program (TypeScript or Python) that declares a `staticSite`, a `service`, a `databases` reference, and a `DatabaseCluster`---about 60 lines of code.
- **What you'll need:** A DigitalOcean account and personal access token, the [Pulumi CLI](/docs/get-started/download-install/), [Node.js](https://nodejs.org/), and a GitHub account with the [DigitalOcean GitHub app](https://cloud.digitalocean.com/apps) installed.
- **Why DigitalOcean:** App Platform's component model maps cleanly to MERN's three tiers, builds and deploys on every Git push, and stays inexpensive at the smallest tier. See the [comparison table](#how-does-digitalocean-compare-to-render-railway-and-flyio-for-mern) below.
Comment on lines +34 to +39
"tool": [
{ "@type": "HowToTool", "name": "Pulumi CLI" },
{ "@type": "HowToTool", "name": "Node.js" },
{ "@type": "HowToTool", "name": "DigitalOcean account and personal access token" },
{ "@type": "HowToTool", "name": "GitHub account with the DigitalOcean GitHub app installed" }
],
Comment on lines +27 to +33
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "Deploy a MERN stack app on DigitalOcean with Pulumi",
"description": "Provision a DigitalOcean App Platform app and Managed MongoDB cluster for a MERN-stack web application using a single Pulumi program in TypeScript or Python.",
"totalTime": "PT15M",
Copy link
Copy Markdown
Contributor

@cnunciato cnunciato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These edits effectively turn this into a completely different post — essentially an example. If we want to do that, we can absolutely create a new post, or a tutorial (and I'd be happy to help with that), but I'd suggest we do that, rather than this.

I'll give this more thorough review tomorrow. I get why we're doing this, and I respect and support the intent, but these are exactly the kinds of sweeping changes I want us not to be making.

@CamSoper CamSoper marked this pull request as draft May 6, 2026 17:23
auto-merge was automatically disabled May 6, 2026 17:23

Pull request was converted to draft

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants