From 9de4f66466da998971484ecc858e1819cd0b9257 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 11:06:01 -0400 Subject: [PATCH 01/14] refacotr blog section to use Jeklly --- .github/workflows/continuous-deployment.yml | 6 + .gitignore | 8 +- Gemfile | 5 + Gemfile.lock | 76 +++++ app/blogs/page.tsx | 74 ---- app/components/Card.tsx | 154 --------- app/components/Navbar.tsx | 6 +- app/services/blogService.ts | 11 - app/types/Post.ts | 8 - blogs/_config.yml | 14 + blogs/_data/categories.yml | 12 + blogs/{content.yml => _data/posts.yml} | 0 blogs/_includes/post-card.html | 38 +++ blogs/_layouts/default.html | 48 +++ blogs/assets/blog.css | 352 ++++++++++++++++++++ blogs/index.html | 25 ++ package.json | 4 +- readme.md | 31 +- 18 files changed, 612 insertions(+), 260 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock delete mode 100644 app/blogs/page.tsx delete mode 100644 app/components/Card.tsx delete mode 100644 app/services/blogService.ts delete mode 100644 app/types/Post.ts create mode 100644 blogs/_config.yml create mode 100644 blogs/_data/categories.yml rename blogs/{content.yml => _data/posts.yml} (100%) create mode 100644 blogs/_includes/post-card.html create mode 100644 blogs/_layouts/default.html create mode 100644 blogs/assets/blog.css create mode 100644 blogs/index.html diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 6614670..18a80a3 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -55,6 +55,11 @@ jobs: with: node-version: 24 cache: npm + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.3 + bundler-cache: true - name: Restore cache uses: actions/cache@v4 with: @@ -70,6 +75,7 @@ jobs: - name: Build with Next.js env: NEXT_BASE_PATH: ${{ github.event.repository.name }} + JEKYLL_BASE_PATH: /${{ github.event.repository.name }}/blogs run: npm run build - name: Download DocumentDB packages from latest release run: .github/scripts/download_packages.sh diff --git a/.gitignore b/.gitignore index ab74a61..d62659b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,12 @@ articles/**/*.yml /.next/ /out/ +# jekyll +/.bundle/ +/.jekyll-cache/ +/.sass-cache/ +/vendor/bundle/ + # production /build @@ -48,4 +54,4 @@ yarn-error.log* # typescript *.tsbuildinfo -next-env.d.ts \ No newline at end of file +next-env.d.ts diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..3009d5d --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem "ffi", "~> 1.15.5" +gem "jekyll", "~> 4.3.4" +gem "webrick", "~> 1.8" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..91f2cd8 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,76 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.9) + public_suffix (>= 2.0.2, < 8.0) + colorator (1.1.0) + concurrent-ruby (1.3.6) + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + eventmachine (1.2.7) + ffi (1.15.5) + forwardable-extended (2.6.0) + google-protobuf (3.23.4) + http_parser.rb (0.8.1) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + jekyll (4.3.4) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (>= 0.3.6, < 0.5) + pathutil (~> 0.9) + rouge (>= 3.0, < 5.0) + safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) + jekyll-sass-converter (3.0.0) + sass-embedded (~> 1.54) + jekyll-watch (2.2.1) + listen (~> 3.0) + kramdown (2.5.2) + rexml (>= 3.4.4) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.10.0) + logger + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.7.0) + mercenary (0.4.0) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (5.1.1) + rake (13.3.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rexml (3.4.4) + rouge (3.30.0) + safe_yaml (1.0.5) + sass-embedded (1.58.3) + google-protobuf (~> 3.21) + rake (>= 10.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + unicode-display_width (2.6.0) + webrick (1.9.2) + +PLATFORMS + ruby + +DEPENDENCIES + ffi (~> 1.15.5) + jekyll (~> 4.3.4) + webrick (~> 1.8) + +BUNDLED WITH + 1.17.2 diff --git a/app/blogs/page.tsx b/app/blogs/page.tsx deleted file mode 100644 index a42308b..0000000 --- a/app/blogs/page.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { getAllPosts } from '../services/blogService'; -import { Card } from '../components/Card'; -import { Post } from '../types/Post'; - -export default function Blogs() { - const posts: Post[] = getAllPosts(); - const featuredPosts = posts.filter(post => post.featured); - const regularPosts = posts.filter(post => !post.featured); - - return ( -
- {/* Artistic background elements */} -
-
-
-
-
-
-
- - {/* Floating particles */} -
-
-
-
-
- -
- {/* Header */} -
-

- Latest from our Blog -

-

- Insights, updates, and deep dives into the world of document databases and open-source innovation -

-
-
- - - {/* Blog Grid */} -
- {/* Featured Posts */} - {featuredPosts.map((post, index) => ( - - ))} - - {/* Regular Posts */} - {regularPosts.map((post, index) => ( - - ))} -
-
-
- ); -} \ No newline at end of file diff --git a/app/components/Card.tsx b/app/components/Card.tsx deleted file mode 100644 index 2fe678d..0000000 --- a/app/components/Card.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { JSX } from 'react'; -import { Post } from '../types/Post'; - -export function Card({ post, featured = false }: { post: Post; featured?: boolean }) { - const icons: Record = { - 'microsoft-open-source-blog': ( - - ), - 'aws-blog': ( - - ), - 'azure-cosmos-db-blog': ( - - ), - 'yugabytedb-blog': ( - - ), - }; - - const icon = icons[post.category as keyof typeof icons]; - - const styles = { - 'microsoft-open-source-blog': { - label: 'Microsoft Open Source Blog', - timestamp: 'August 25, 2025', - gradientFrom: 'from-blue-500', - gradientTo: 'to-blue-600', - textColor: 'blue-400', - hoverColor: 'blue-300', - bgGradient: 'from-blue-500/10 to-purple-500/10', - borderHover: 'border-blue-500/50', - tagColors: [ - 'bg-blue-500/20 text-blue-400', - 'bg-purple-500/20 text-purple-400', - 'bg-green-500/20 text-green-400', - 'bg-orange-500/20 text-orange-400', - ], - }, - 'aws-blog': { - label: 'AWS Blogs', - timestamp: 'Recent', - gradientFrom: 'from-orange-500', - gradientTo: 'to-orange-600', - textColor: 'orange-400', - hoverColor: 'orange-300', - bgGradient: 'from-orange-500/10 to-amber-500/10', - borderHover: 'border-orange-500/50', - tagColors: [ - 'bg-orange-500/20 text-orange-400', - 'bg-amber-500/20 text-amber-400', - 'bg-yellow-500/20 text-yellow-400', - 'bg-blue-500/20 text-blue-400', - ], - }, - 'azure-cosmos-db-blog': { - label: 'Azure Cosmos DB Blog', - timestamp: 'Recent', - gradientFrom: 'from-purple-500', - gradientTo: 'to-purple-600', - textColor: 'purple-400', - hoverColor: 'purple-300', - bgGradient: 'from-purple-500/10 to-pink-500/10', - borderHover: 'border-purple-500/50', - tagColors: [ - 'bg-purple-500/20 text-purple-400', - 'bg-blue-500/20 text-blue-400', - 'bg-green-500/20 text-green-400', - 'bg-orange-500/20 text-orange-400', - ], - }, - 'yugabytedb-blog': { - label: 'YugabyteDB Blog', - timestamp: 'Partner Content', - gradientFrom: 'from-green-500', - gradientTo: 'to-green-600', - textColor: 'green-400', - hoverColor: 'green-300', - bgGradient: 'from-green-500/10 to-emerald-500/10', - borderHover: 'border-green-500/50', - tagColors: [ - 'bg-green-500/20 text-green-400', - 'bg-blue-500/20 text-blue-400', - 'bg-orange-500/20 text-orange-400', - 'bg-yellow-500/20 text-yellow-400', - ], - }, - }; - - const style = styles[post.category as keyof typeof styles]; - - return ( - - ); -} diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx index 048c031..3441467 100644 --- a/app/components/Navbar.tsx +++ b/app/components/Navbar.tsx @@ -75,12 +75,12 @@ export default function Navbar() { > Download - Blogs - + diff --git a/app/services/blogService.ts b/app/services/blogService.ts deleted file mode 100644 index 82c58a8..0000000 --- a/app/services/blogService.ts +++ /dev/null @@ -1,11 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import yaml from 'js-yaml'; -import { Post } from '../types/Post'; - -export function getAllPosts(): Post[] { - const filePath = path.join(process.cwd(), 'blogs', 'content.yml'); - const fileContents = fs.readFileSync(filePath, 'utf8'); - const posts = yaml.load(fileContents) as Post[]; - return posts; -} \ No newline at end of file diff --git a/app/types/Post.ts b/app/types/Post.ts deleted file mode 100644 index 23419f0..0000000 --- a/app/types/Post.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface Post { - title: string; - category: string; - description: string; - tags: string[]; - featured?: boolean; - uri: string; -} \ No newline at end of file diff --git a/blogs/_config.yml b/blogs/_config.yml new file mode 100644 index 0000000..133e681 --- /dev/null +++ b/blogs/_config.yml @@ -0,0 +1,14 @@ +title: DocumentDB Blog +description: Insights, updates, and deep dives into the world of document databases and open-source innovation. +baseurl: /blogs +url: "" +permalink: pretty +markdown: kramdown +kramdown: + input: GFM +defaults: + - + scope: + path: "" + values: + layout: default diff --git a/blogs/_data/categories.yml b/blogs/_data/categories.yml new file mode 100644 index 0000000..682eb6f --- /dev/null +++ b/blogs/_data/categories.yml @@ -0,0 +1,12 @@ +microsoft-open-source-blog: + label: Microsoft Open Source Blog + meta: August 25, 2025 +aws-blog: + label: AWS Blogs + meta: Recent +azure-cosmos-db-blog: + label: Azure Cosmos DB Blog + meta: Recent +yugabytedb-blog: + label: YugabyteDB Blog + meta: Partner Content diff --git a/blogs/content.yml b/blogs/_data/posts.yml similarity index 100% rename from blogs/content.yml rename to blogs/_data/posts.yml diff --git a/blogs/_includes/post-card.html b/blogs/_includes/post-card.html new file mode 100644 index 0000000..2db22c5 --- /dev/null +++ b/blogs/_includes/post-card.html @@ -0,0 +1,38 @@ +{% assign post = include.post %} +{% assign category = site.data.categories[post.category] %} + + diff --git a/blogs/_layouts/default.html b/blogs/_layouts/default.html new file mode 100644 index 0000000..91df859 --- /dev/null +++ b/blogs/_layouts/default.html @@ -0,0 +1,48 @@ + + + + + + {% if page.title %}{{ page.title }} · {% endif %}DocumentDB + + + + + + {% assign site_root = site.baseurl | replace: '/blogs', '' %} + {% if site_root == '' %} + {% assign site_root = '/' %} + {% endif %} + + {% assign home_href = site_root | append: '/' | replace: '//', '/' %} + {% assign docs_href = site_root | append: '/docs/' | replace: '//', '/' %} + {% assign packages_href = site_root | append: '/packages/' | replace: '//', '/' %} + {% assign blogs_href = site.baseurl | append: '/' | replace: '//', '/' %} + +
+ + +
+ {{ content }} +
+ +
+ +
+
+ + diff --git a/blogs/assets/blog.css b/blogs/assets/blog.css new file mode 100644 index 0000000..0827f49 --- /dev/null +++ b/blogs/assets/blog.css @@ -0,0 +1,352 @@ +:root { + --page-bg: #09090b; + --surface: rgba(23, 23, 27, 0.84); + --surface-strong: #111827; + --surface-soft: rgba(38, 38, 45, 0.82); + --text: #f5f7fb; + --muted: #a7afbe; + --subtle: #7c8699; + --border: rgba(148, 163, 184, 0.16); + --shadow: rgba(2, 6, 23, 0.5); +} + +* { + box-sizing: border-box; +} + +html { + min-height: 100%; +} + +body { + margin: 0; + min-height: 100%; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + color: var(--text); + background: + radial-gradient(circle at top left, rgba(59, 130, 246, 0.18), transparent 30%), + radial-gradient(circle at top right, rgba(168, 85, 247, 0.14), transparent 28%), + radial-gradient(circle at bottom center, rgba(34, 197, 94, 0.1), transparent 26%), + var(--page-bg); +} + +a { + color: inherit; + text-decoration: none; +} + +.site-shell { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.site-header { + position: sticky; + top: 0; + z-index: 40; + border-bottom: 1px solid var(--border); + background: rgba(9, 9, 11, 0.88); + backdrop-filter: blur(14px); +} + +.site-header__inner, +.site-footer__inner { + width: min(1100px, calc(100% - 2rem)); + margin: 0 auto; +} + +.site-header__inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; + padding: 1rem 0; +} + +.site-brand { + font-size: 1.35rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.site-nav { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: 1rem; + color: var(--muted); +} + +.site-nav a { + transition: color 160ms ease; +} + +.site-nav a:hover, +.site-nav a[aria-current="page"] { + color: var(--text); +} + +.blog-main { + flex: 1; + padding: 4.5rem 1rem 5rem; +} + +.blog-hero, +.posts-list { + width: min(1100px, 100%); + margin: 0 auto; +} + +.blog-hero { + text-align: center; + margin-bottom: 2.75rem; +} + +.blog-hero__kicker { + margin: 0 0 0.85rem; + font-size: 0.95rem; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: #93c5fd; +} + +.blog-hero__title { + margin: 0; + font-size: clamp(2.75rem, 6vw, 4.5rem); + line-height: 1.05; + letter-spacing: -0.05em; +} + +.blog-hero__description { + max-width: 760px; + margin: 1.3rem auto 0; + font-size: clamp(1.05rem, 2vw, 1.25rem); + line-height: 1.7; + color: var(--muted); +} + +.blog-hero__note { + display: inline-flex; + margin-top: 1.6rem; + padding: 0.6rem 1rem; + border-radius: 999px; + border: 1px solid rgba(96, 165, 250, 0.25); + background: rgba(59, 130, 246, 0.08); + color: #cbd5e1; + font-size: 0.95rem; +} + +.posts-list { + display: grid; + gap: 1.35rem; +} + +.post-card { + --accent-rgb: 96, 165, 250; + --accent-alt-rgb: 37, 99, 235; + display: block; + position: relative; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 24px; + background: linear-gradient(180deg, rgba(17, 24, 39, 0.88), rgba(10, 10, 11, 0.94)); + box-shadow: 0 24px 60px -28px var(--shadow); + cursor: pointer; + transition: + transform 180ms ease, + border-color 180ms ease, + box-shadow 180ms ease, + color 180ms ease; +} + +.post-card::before { + content: ""; + position: absolute; + inset: -30%; + background: + radial-gradient(circle at top right, rgba(var(--accent-rgb), 0.22), transparent 24%), + radial-gradient(circle at bottom left, rgba(var(--accent-alt-rgb), 0.18), transparent 28%); + pointer-events: none; +} + +.post-card:hover, +.post-card:focus-visible { + transform: translateY(-3px); + border-color: rgba(var(--accent-rgb), 0.45); + box-shadow: 0 30px 65px -28px rgba(var(--accent-rgb), 0.22); +} + +.post-card:focus-visible { + outline: 3px solid rgba(var(--accent-rgb), 0.35); + outline-offset: 3px; +} + +.post-card__body { + position: relative; + padding: 1.75rem; +} + +.post-card--featured .post-card__body { + padding: 2.3rem; +} + +.post-card__header { + display: flex; + align-items: center; + gap: 0.95rem; + margin-bottom: 1.25rem; +} + +.post-card__icon { + flex: 0 0 auto; + width: 2.75rem; + height: 2.75rem; + border-radius: 0.95rem; + background: linear-gradient(135deg, rgba(var(--accent-rgb), 1), rgba(var(--accent-alt-rgb), 1)); + box-shadow: 0 18px 32px -20px rgba(var(--accent-rgb), 0.85); +} + +.post-card__source, +.post-card__meta { + margin: 0; +} + +.post-card__source { + font-size: 0.95rem; + font-weight: 600; + color: rgb(var(--accent-rgb)); +} + +.post-card__meta { + margin-top: 0.15rem; + color: var(--subtle); + font-size: 0.8rem; +} + +.post-card__title { + margin: 0; + font-size: 1.45rem; + line-height: 1.18; + letter-spacing: -0.03em; + transition: color 180ms ease; +} + +.post-card--featured .post-card__title { + font-size: clamp(1.9rem, 3vw, 2.5rem); +} + +.post-card__description { + margin: 1rem 0 0; + color: var(--muted); + line-height: 1.75; +} + +.post-card__tags { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; + margin-top: 1.4rem; +} + +.post-card__tag { + display: inline-flex; + align-items: center; + padding: 0.45rem 0.8rem; + border-radius: 999px; + background: rgba(var(--accent-rgb), 0.14); + color: rgb(var(--accent-rgb)); + font-size: 0.8rem; + font-weight: 600; +} + +.post-card__link { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-top: 1.45rem; + font-weight: 600; + color: rgb(var(--accent-rgb)); +} + +.post-card:hover .post-card__title, +.post-card:focus-visible .post-card__title, +.post-card:hover .post-card__link, +.post-card:focus-visible .post-card__link { + color: rgb(var(--accent-alt-rgb)); +} + +.post-card__link span[aria-hidden="true"] { + transition: transform 180ms ease; +} + +.post-card:hover .post-card__link span[aria-hidden="true"], +.post-card:focus-visible .post-card__link span[aria-hidden="true"] { + transform: translateX(3px); +} + +.post-card--microsoft-open-source-blog { + --accent-rgb: 96, 165, 250; + --accent-alt-rgb: 147, 51, 234; +} + +.post-card--aws-blog { + --accent-rgb: 249, 115, 22; + --accent-alt-rgb: 245, 158, 11; +} + +.post-card--azure-cosmos-db-blog { + --accent-rgb: 168, 85, 247; + --accent-alt-rgb: 59, 130, 246; +} + +.post-card--yugabytedb-blog { + --accent-rgb: 34, 197, 94; + --accent-alt-rgb: 16, 185, 129; +} + +.site-footer { + border-top: 1px solid var(--border); + background: rgba(9, 9, 11, 0.9); +} + +.site-footer__inner { + padding: 1.2rem 0 1.4rem; + color: var(--subtle); + font-size: 0.85rem; + line-height: 1.6; + text-align: center; +} + +@media (max-width: 820px) { + .site-header__inner { + flex-direction: column; + align-items: flex-start; + } + + .site-nav { + justify-content: flex-start; + } + + .blog-main { + padding-top: 3rem; + } +} + +@media (max-width: 640px) { + .site-header__inner, + .site-footer__inner { + width: min(1100px, calc(100% - 1.5rem)); + } + + .post-card__body, + .post-card--featured .post-card__body { + padding: 1.35rem; + } + + .post-card__header { + align-items: flex-start; + } +} diff --git a/blogs/index.html b/blogs/index.html new file mode 100644 index 0000000..34c3e8c --- /dev/null +++ b/blogs/index.html @@ -0,0 +1,25 @@ +--- +title: Latest from our Blog +description: Insights, updates, and deep dives into the world of document databases and open-source innovation. +--- +{% assign featured_posts = site.data.posts | where: "featured", true %} +{% assign regular_posts = site.data.posts | where_exp: "post", "post.featured != true" %} + +
+

Community insights

+

Latest from our Blog

+

+ Insights, updates, and deep dives into the world of document databases and open-source innovation. +

+

Articles open on their original publisher sites in a new tab.

+
+ +
+ {% for post in featured_posts %} + {% include post-card.html post=post featured=true %} + {% endfor %} + + {% for post in regular_posts %} + {% include post-card.html post=post featured=false %} + {% endfor %} +
diff --git a/package.json b/package.json index 2c332ae..0dfcd18 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "private": true, "scripts": { "dev": "npm run compile && next dev --turbopack", - "build": "npm run compile && next build --turbopack", + "build": "npm run build:next && npm run build:blogs", + "build:next": "npm run compile && next build --turbopack", + "build:blogs": "BUNDLE_FORCE_RUBY_PLATFORM=true bundle exec jekyll build --config blogs/_config.yml --source blogs --destination out/blogs --baseurl \"${JEKYLL_BASE_PATH:-/blogs}\"", "compile": "npm run compile:clean && npm run compile:content", "compile:content": "tsx scripts/compile-content.tsx", "compile:clean": "tsx scripts/compile-clean.tsx", diff --git a/readme.md b/readme.md index 422c9e3..0b987bf 100644 --- a/readme.md +++ b/readme.md @@ -1,11 +1,13 @@ # DocumentDB Website -A modern website for DocumentDB built with [Next.js](https://nextjs.org/) and [Tailwind CSS](https://tailwindcss.com/). The site features community blog posts and technical documentation, with content automatically pulled from the [documentdb/docs](https://github.com/documentdb/docs) repository during the build process. +A modern website for DocumentDB built with [Next.js](https://nextjs.org/) for the main site and [Jekyll](https://jekyllrb.com/) for the `/blogs/` section. The site features community blog posts and technical documentation, with content automatically pulled from the [documentdb/docs](https://github.com/documentdb/docs) repository during the build process. ## Prerequisites for Development - **[Node.js](https://nodejs.org/)** (*20 or higher*) +- **[Ruby](https://www.ruby-lang.org/)** with **Bundler** (*for the Jekyll-powered blog section*) + - **[Git](https://git-scm.com/)** (*for cloning documentation content*) You can develop locally on any machine with these prerequisites installed, or use [GitHub Codespaces](#develop-in-github-codespaces) for a pre-configured environment. @@ -22,13 +24,26 @@ Get started by cloning and running this repository locally. npm install ``` -1. Start the development server: +1. Install Ruby dependencies: + + ```bash + BUNDLE_FORCE_RUBY_PLATFORM=true bundle install + ``` + +1. Start the Next.js development server: ```bash npm run dev ``` -1. Observe that the site will be available at http://localhost:3000 +1. For a full static preview, including the Jekyll-powered blog section: + + ```bash + npm run build + npm run start + ``` + +1. Observe that the preview site will be available at http://localhost:3000 The first time you run `npm run dev` or `npm run build`, documentation content will be automatically compiled from the [documentdb/docs](https://github.com/documentdb/docs) repository. @@ -40,7 +55,7 @@ We welcome contributions to improve the DocumentDB website, whether it's blog po Blog posts are managed locally in this repository. Contribute directly through a pull request to this repository. -1. Open [blogs/content.yml](blogs/content.yml) +1. Open [blogs/_data/posts.yml](blogs/_data/posts.yml) 1. Add your blog post entry following the format of existing posts @@ -100,13 +115,13 @@ The `content.config.json` file controls how documentation is compiled from exter While content is automatically compiled during builds, you can manually trigger these operations during development: ```bash -# Clean all target directories -npm run clean:content +# Build the full static site artifact +npm run build ``` ```bash -# Compile content from sources -npm run compile:content +# Start the static preview server +npm run start ``` ## Develop in GitHub Codespaces From 3ee41e78d0a9ddd1a495ee87a15f54b7dd059577 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 12:49:47 -0400 Subject: [PATCH 02/14] Add new blogpost --- blogs/_config.yml | 8 + blogs/_data/categories.yml | 3 + blogs/_includes/post-card.html | 50 +++- blogs/_layouts/post.html | 53 ++++ ...-19-meet-documentdb-kubernetes-operator.md | 91 ++++++ blogs/assets/blog.css | 277 +++++++++++++++++- blogs/index.html | 62 +++- readme.md | 44 ++- 8 files changed, 558 insertions(+), 30 deletions(-) create mode 100644 blogs/_layouts/post.html create mode 100644 blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md diff --git a/blogs/_config.yml b/blogs/_config.yml index 133e681..f9503d1 100644 --- a/blogs/_config.yml +++ b/blogs/_config.yml @@ -12,3 +12,11 @@ defaults: path: "" values: layout: default + - + scope: + path: "" + type: posts + values: + layout: post + category: documentdb-blog + permalink: /posts/:title/ diff --git a/blogs/_data/categories.yml b/blogs/_data/categories.yml index 682eb6f..65e72ca 100644 --- a/blogs/_data/categories.yml +++ b/blogs/_data/categories.yml @@ -1,3 +1,6 @@ +documentdb-blog: + label: DocumentDB Blog + meta: Published here microsoft-open-source-blog: label: Microsoft Open Source Blog meta: August 25, 2025 diff --git a/blogs/_includes/post-card.html b/blogs/_includes/post-card.html index 2db22c5..79ffd0f 100644 --- a/blogs/_includes/post-card.html +++ b/blogs/_includes/post-card.html @@ -1,25 +1,57 @@ {% assign post = include.post %} {% assign category = site.data.categories[post.category] %} +{% assign description = post.description | default: post.excerpt | strip_html | strip_newlines | strip %} + +{% if post.date %} + {% assign meta_text = post.date | date: "%B %-d, %Y" %} +{% elsif post.meta %} + {% assign meta_text = post.meta %} +{% else %} + {% assign meta_text = category.meta | default: "Community article" %} +{% endif %} + +{% if post.uri %} + {% assign href = post.uri %} + {% assign is_external = true %} +{% else %} + {% assign href = post.url | relative_url %} + {% assign is_external = false %} +{% endif %}
+ {% if post.cover_image %} + {% if post.cover_image contains '://' %} + {% assign cover_image_src = post.cover_image %} + {% else %} + {% assign cover_image_src = post.cover_image | relative_url %} + {% endif %} +
+ {{ post.cover_image_alt | default: post.title | escape }} +
+ {% endif %} +

{{ category.label | default: post.category }}

-

{{ category.meta | default: "Community article" }}

+

{{ meta_text }}

{{ post.title }}

-

{{ post.description }}

+

{{ description }}

{% if post.tags and post.tags.size > 0 %}
@@ -30,7 +62,11 @@

{{ post.title }}

{% endif %} - {% if include.featured %}Read full article{% else %}Read more{% endif %} + {% if is_external %} + {% if include.featured %}Read full article{% else %}Read more{% endif %} + {% else %} + {% if include.featured %}Read featured post{% else %}Read post{% endif %} + {% endif %}
diff --git a/blogs/_layouts/post.html b/blogs/_layouts/post.html new file mode 100644 index 0000000..13f702b --- /dev/null +++ b/blogs/_layouts/post.html @@ -0,0 +1,53 @@ +--- +layout: default +--- +{% assign category = site.data.categories[page.category] %} + +
+

+ ← Back to all blog posts +

+ +
+

{{ category.label | default: "DocumentDB Blog" }}

+

{{ page.title }}

+ + + + {% if page.description %} +

{{ page.description }}

+ {% endif %} + + {% if page.tags and page.tags.size > 0 %} + + {% endif %} +
+ + {% if page.cover_image %} + {% if page.cover_image contains '://' %} + {% assign cover_image_src = page.cover_image %} + {% else %} + {% assign cover_image_src = page.cover_image | relative_url %} + {% endif %} +
+ {{ page.cover_image_alt | default: page.title | escape }} +
+ {% endif %} + +
+
+ {{ content }} +
+
+
diff --git a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md new file mode 100644 index 0000000..46aaf2a --- /dev/null +++ b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md @@ -0,0 +1,91 @@ +--- +title: Meet the DocumentDB Kubernetes Operator +description: Preview an open-source operator that brings declarative deployment, secure connectivity, high availability, backup and restore, and multi-environment deployment patterns to DocumentDB on Kubernetes. +date: 2026-03-19 +featured: true +author: DocumentDB team +category: documentdb-blog +tags: + - Kubernetes + - Operator + - DocumentDB + - Open Source +--- +Running a document database on Kubernetes should not require a maze of hand-built scripts for failover, certificate handling, backup jobs, and day-two troubleshooting. The new DocumentDB Kubernetes Operator is designed to turn that operational work into a Kubernetes-native workflow. + +DocumentDB is the engine powering vCore-based Azure Cosmos DB for MongoDB. Built on PostgreSQL, it provides a native document-oriented NoSQL database with support for CRUD operations on BSON data types. The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the DocumentDB Gateway, and the supporting Kubernetes resources around them. Because the gateway enables MongoDB-compatible drivers, APIs, and tools, teams can keep working with familiar clients such as `mongosh`. + +## Why this matters + +Kubernetes teams have strong patterns for stateless applications, but data services often still depend on custom runbooks and fragile operational glue. The DocumentDB Kubernetes Operator closes that gap for DocumentDB by giving platform teams a single declarative control surface for deployment, availability, security, and recovery. + +With the preview release, you can: + +- install the operator with Helm +- have the Helm chart install CloudNativePG as a dependency +- deploy DocumentDB through a Kubernetes custom resource +- connect with `mongosh` and other MongoDB-compatible tooling +- manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs + +## From quickstart to useful data fast + +The quickstart is intentionally direct. Install `cert-manager`, install the operator, create a Secret for gateway credentials, and apply a `DocumentDB` resource. The public preview docs target Kubernetes 1.35+ and call out local development with `kind` (v0.31+) and `minikube`, while also showing cloud-friendly access patterns through `LoadBalancer` services on AKS, EKS, and GKE. + +At the heart of that flow is a simple custom resource: + +```yaml +apiVersion: documentdb.io/preview +kind: DocumentDB +metadata: + name: documentdb-preview + namespace: documentdb-preview-ns +spec: + nodeCount: 1 + instancesPerNode: 1 + documentDbCredentialSecret: documentdb-credentials + resource: + storage: + pvcSize: 10Gi + exposeViaService: + serviceType: ClusterIP +``` + +Once the cluster reports a healthy state, you can connect locally with port forwarding or expose it through a load balancer in supported environments. The result is a much shorter path from cluster creation to a live MongoDB-compatible endpoint. + +## Built for day-two operations + +Bringing up a cluster is only the beginning, so the operator is opinionated about day-two workflows as well. + +Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability and automatic failover. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups, retention policies, and restore workflows into a new cluster. And when operators need visibility into what is happening, the `kubectl documentdb` plugin adds purpose-built commands for status inspection, event triage, and primary promotion. + +That makes the operator interesting not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. + +## Security and connectivity without reinventing the basics + +Secure connectivity is built into the model from the start. The DocumentDB gateway always encrypts client connections; the TLS mode controls how certificates are managed. In practice, that means teams can choose the workflow that matches their environment: + +- `SelfSigned` for development and test environments +- `CertManager` for clusters that already standardize on cert-manager +- `Provided` for organizations that manage certificates through their own PKI processes + +The same pattern carries into networking. The docs cover local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services where that model makes sense. The operator keeps those choices declarative instead of forcing each team to reinvent them. + +## A practical path to multi-environment deployment + +The public docs position the operator across multiple cloud environments and Kubernetes distributions, and the multi-cluster guidance goes further with a documented KubeFleet-based deployment pattern spanning AKS and an on-premises Kubernetes cluster. That is a meaningful direction for teams that want Kubernetes-native control over data placement and replication without committing their operational model to a single environment. + +Just as importantly, the operator keeps sensitive workflows explicit. Promotion is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow, which is often exactly the right design when changes cross infrastructure or compliance boundaries. + +## Why try it now + +The project is still in preview, and the public docs are clear that it is not yet recommended for production workloads. That is exactly why now is the right time to evaluate it. + +If you want to see how DocumentDB could fit into your Kubernetes platform, this preview gives you a concrete way to do it: start locally, inspect the control model, test backup and restore, evaluate TLS options, and explore the operator experience before general availability. + +## Start exploring + +- [Quickstart guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/index.md) +- [Backup and restore guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/backup-and-restore.md) +- [kubectl-documentdb plugin](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/kubectl-plugin.md) +- [TLS configuration guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/configuration/tls.md) +- [Multi-cluster deployment guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/developer-guides/manual-multi-cloud-deployment-guide.md) diff --git a/blogs/assets/blog.css b/blogs/assets/blog.css index 0827f49..5c3fd5c 100644 --- a/blogs/assets/blog.css +++ b/blogs/assets/blog.css @@ -94,7 +94,9 @@ a { } .blog-hero, -.posts-list { +.posts-group, +.posts-list, +.blog-post { width: min(1100px, 100%); margin: 0 auto; } @@ -128,15 +130,25 @@ a { color: var(--muted); } -.blog-hero__note { - display: inline-flex; - margin-top: 1.6rem; - padding: 0.6rem 1rem; - border-radius: 999px; - border: 1px solid rgba(96, 165, 250, 0.25); - background: rgba(59, 130, 246, 0.08); - color: #cbd5e1; - font-size: 0.95rem; +.posts-group { + margin-bottom: 3rem; +} + +.posts-group__header { + margin-bottom: 1.1rem; +} + +.posts-group__title { + margin: 0; + font-size: clamp(1.45rem, 2.2vw, 1.9rem); + letter-spacing: -0.03em; +} + +.posts-group__description { + margin: 0.45rem 0 0; + max-width: 48rem; + color: var(--muted); + line-height: 1.7; } .posts-list { @@ -189,6 +201,25 @@ a { padding: 1.75rem; } +.post-card__media { + position: relative; + aspect-ratio: 16 / 9; + overflow: hidden; + border-bottom: 1px solid rgba(148, 163, 184, 0.14); + background: rgba(15, 23, 42, 0.8); +} + +.post-card__image { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.post-card--featured .post-card__media { + aspect-ratio: 16 / 8; +} + .post-card--featured .post-card__body { padding: 2.3rem; } @@ -307,6 +338,227 @@ a { --accent-alt-rgb: 16, 185, 129; } +.post-card--documentdb-blog { + --accent-rgb: 14, 165, 233; + --accent-alt-rgb: 59, 130, 246; +} + +.blog-post { + max-width: 840px; +} + +.blog-post__back { + margin: 0 0 1.2rem; +} + +.blog-post__back a { + color: var(--muted); + font-weight: 600; + transition: color 160ms ease; +} + +.blog-post__back a:hover { + color: var(--text); +} + +.blog-post__header, +.blog-post__content-shell { + border: 1px solid var(--border); + border-radius: 28px; + background: linear-gradient(180deg, rgba(17, 24, 39, 0.82), rgba(10, 10, 11, 0.9)); + box-shadow: 0 24px 60px -28px var(--shadow); +} + +.blog-post__header { + padding: 2rem; +} + +.blog-post__eyebrow { + margin: 0; + font-size: 0.92rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: #7dd3fc; +} + +.blog-post__title { + margin: 0.75rem 0 0; + font-size: clamp(2.4rem, 5vw, 3.8rem); + line-height: 1.04; + letter-spacing: -0.05em; +} + +.blog-post__meta { + display: flex; + flex-wrap: wrap; + gap: 0.85rem; + margin-top: 1rem; + color: var(--subtle); + font-size: 0.95rem; +} + +.blog-post__lead { + margin: 1rem 0 0; + color: var(--muted); + font-size: 1.08rem; + line-height: 1.75; +} + +.blog-post__tags { + display: flex; + flex-wrap: wrap; + gap: 0.65rem; + margin-top: 1.35rem; +} + +.blog-post__tag { + display: inline-flex; + align-items: center; + padding: 0.45rem 0.8rem; + border-radius: 999px; + background: rgba(14, 165, 233, 0.14); + color: #7dd3fc; + font-size: 0.8rem; + font-weight: 600; +} + +.blog-post__content-shell { + margin-top: 1.5rem; + padding: 2rem; +} + +.blog-post__hero { + margin-top: 1.5rem; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 28px; + background: rgba(15, 23, 42, 0.85); + box-shadow: 0 24px 60px -28px var(--shadow); +} + +.blog-post__hero-image { + display: block; + width: 100%; + height: auto; +} + +.blog-post__content { + color: #e5e7eb; + line-height: 1.8; + font-size: 1.05rem; +} + +.blog-post__content > :first-child { + margin-top: 0; +} + +.blog-post__content > :last-child { + margin-bottom: 0; +} + +.blog-post__content h2, +.blog-post__content h3, +.blog-post__content h4 { + margin-top: 2.25rem; + margin-bottom: 0.8rem; + line-height: 1.2; + letter-spacing: -0.03em; +} + +.blog-post__content h2 { + font-size: 1.75rem; +} + +.blog-post__content h3 { + font-size: 1.35rem; +} + +.blog-post__content p, +.blog-post__content ul, +.blog-post__content ol, +.blog-post__content blockquote, +.blog-post__content pre, +.blog-post__content table { + margin: 1.2rem 0; +} + +.blog-post__content ul, +.blog-post__content ol { + padding-left: 1.5rem; +} + +.blog-post__content li + li { + margin-top: 0.45rem; +} + +.blog-post__content a { + color: #93c5fd; + text-decoration: underline; + text-decoration-color: rgba(147, 197, 253, 0.35); + text-underline-offset: 0.18em; +} + +.blog-post__content a:hover { + color: #bfdbfe; +} + +.blog-post__content blockquote { + padding: 1rem 1.25rem; + border-left: 4px solid rgba(59, 130, 246, 0.7); + border-radius: 16px; + background: rgba(30, 41, 59, 0.55); + color: #dbeafe; +} + +.blog-post__content pre { + overflow-x: auto; + padding: 1rem 1.15rem; + border-radius: 18px; + border: 1px solid rgba(148, 163, 184, 0.16); + background: #0f172a; +} + +.blog-post__content code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 0.92em; +} + +.blog-post__content :not(pre) > code { + padding: 0.18rem 0.42rem; + border-radius: 10px; + background: rgba(30, 41, 59, 0.75); +} + +.blog-post__content img { + display: block; + width: 100%; + max-width: 100%; + margin: 1.5rem 0; + border: 1px solid rgba(148, 163, 184, 0.16); + border-radius: 24px; + background: rgba(15, 23, 42, 0.82); + box-shadow: 0 24px 60px -28px var(--shadow); +} + +.blog-post__content table { + width: 100%; + border-collapse: collapse; +} + +.blog-post__content th, +.blog-post__content td { + padding: 0.75rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.16); + text-align: left; +} + +.blog-post__content hr { + margin: 2rem 0; + border: 0; + border-top: 1px solid rgba(148, 163, 184, 0.16); +} + .site-footer { border-top: 1px solid var(--border); background: rgba(9, 9, 11, 0.9); @@ -333,6 +585,11 @@ a { .blog-main { padding-top: 3rem; } + + .blog-post__header, + .blog-post__content-shell { + padding: 1.5rem; + } } @media (max-width: 640px) { diff --git a/blogs/index.html b/blogs/index.html index 34c3e8c..01d3efb 100644 --- a/blogs/index.html +++ b/blogs/index.html @@ -2,8 +2,10 @@ title: Latest from our Blog description: Insights, updates, and deep dives into the world of document databases and open-source innovation. --- -{% assign featured_posts = site.data.posts | where: "featured", true %} -{% assign regular_posts = site.data.posts | where_exp: "post", "post.featured != true" %} +{% assign featured_local_posts = site.posts | where: "featured", true %} +{% assign featured_external_posts = site.data.posts | where: "featured", true %} +{% assign local_posts = site.posts | where_exp: "post", "post.featured != true" %} +{% assign external_posts = site.data.posts | where_exp: "post", "post.featured != true" %}

Community insights

@@ -11,15 +13,53 @@

Latest from our Blog

Insights, updates, and deep dives into the world of document databases and open-source innovation.

-

Articles open on their original publisher sites in a new tab.

-
- {% for post in featured_posts %} - {% include post-card.html post=post featured=true %} - {% endfor %} +{% if featured_local_posts.size > 0 or featured_external_posts.size > 0 %} +
+
+ +

Highlighted posts and important community announcements.

+
- {% for post in regular_posts %} - {% include post-card.html post=post featured=false %} - {% endfor %} -
+
+ {% for post in featured_local_posts %} + {% include post-card.html post=post featured=true %} + {% endfor %} + + {% for post in featured_external_posts %} + {% include post-card.html post=post featured=true %} + {% endfor %} +
+
+{% endif %} + +{% if local_posts.size > 0 %} +
+
+

Published here

+

Markdown posts maintained directly in this repository and rendered with the same card style as the rest of the blog.

+
+ +
+ {% for post in local_posts %} + {% include post-card.html post=post featured=false %} + {% endfor %} +
+
+{% endif %} + +{% if external_posts.size > 0 %} +
+
+

From around the web

+

Curated partner and community articles hosted outside this repository.

+
+ +
+ {% for post in external_posts %} + {% include post-card.html post=post featured=false %} + {% endfor %} +
+
+{% endif %} diff --git a/readme.md b/readme.md index 0b987bf..46e9c79 100644 --- a/readme.md +++ b/readme.md @@ -55,9 +55,49 @@ We welcome contributions to improve the DocumentDB website, whether it's blog po Blog posts are managed locally in this repository. Contribute directly through a pull request to this repository. -1. Open [blogs/_data/posts.yml](blogs/_data/posts.yml) +You can publish blog content in two ways: -1. Add your blog post entry following the format of existing posts +1. **Markdown posts hosted in this repo** + + Add a new file under `blogs/_posts/` using the Jekyll naming format: + + ```text + blogs/_posts/YYYY-MM-DD-my-post-title.md + ``` + + Start it with front matter like: + + ```yaml + --- + title: My Blog Post + description: One-line summary used in the blog card. + date: 2026-03-19 + featured: false + author: Your Name + category: documentdb-blog + cover_image: /assets/images/posts/my-post-title/hero.png + cover_image_alt: Short description of the image + tags: + - Example + - Markdown + --- + ``` + + Store post images under: + + ```text + blogs/assets/images/posts/my-post-title/ + ``` + + Then write the post body in Markdown. Jekyll renders the post page and uses the same card layout on the blog index. Images can be referenced directly from Markdown, for example: + + ```md + ![Architecture diagram]({{ '/assets/images/posts/my-post-title/diagram.png' | relative_url }}) + ``` + +1. **Curated external articles** + + Open [blogs/_data/posts.yml](blogs/_data/posts.yml) and add a new entry following the existing YAML format when you want the card to link to an article hosted elsewhere. 1. Submit a pull request for review From 5f060ddb4f6e4a5f5fb4c02284244efbdd5d7f57 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:07:12 -0400 Subject: [PATCH 03/14] Fix CI for Jekyll blog build Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ee09c25..72ef7b5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -41,6 +41,12 @@ jobs: with: node-version: 24 cache: npm + - name: Setup Ruby + # The full site build now includes the Jekyll-powered blogs section. + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.3 + bundler-cache: true - name: Install dependencies run: npm ci - name: Build Next.js site From 959c75cc8f57b58552f81d1094a0c8308d8d32a1 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:13:09 -0400 Subject: [PATCH 04/14] Update Bundler lockfile for Ruby 3.3 CI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 91f2cd8..acc265d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -73,4 +73,4 @@ DEPENDENCIES webrick (~> 1.8) BUNDLED WITH - 1.17.2 + 2.4.22 From a27f18e9acab95fbe071afe93be561264739909a Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:27:48 -0400 Subject: [PATCH 05/14] Align Bundler platform in CI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/continuous-deployment.yml | 2 ++ .github/workflows/continuous-integration.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 18a80a3..23e02d1 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -20,6 +20,8 @@ jobs: permissions: contents: read runs-on: ubuntu-22.04 + env: + BUNDLE_FORCE_RUBY_PLATFORM: true steps: - name: Checkout source uses: actions/checkout@v5 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 72ef7b5..10e325f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -33,6 +33,8 @@ jobs: build-validation: name: Validate Next.js build runs-on: ubuntu-latest + env: + BUNDLE_FORCE_RUBY_PLATFORM: true steps: - name: Checkout repository uses: actions/checkout@v5 From d652d8da8f74013fe96fb955f5440712699d5e7d Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:29:50 -0400 Subject: [PATCH 06/14] Remove forced Bundler platform Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/continuous-deployment.yml | 2 -- .github/workflows/continuous-integration.yml | 2 -- package.json | 2 +- readme.md | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-deployment.yml b/.github/workflows/continuous-deployment.yml index 23e02d1..18a80a3 100644 --- a/.github/workflows/continuous-deployment.yml +++ b/.github/workflows/continuous-deployment.yml @@ -20,8 +20,6 @@ jobs: permissions: contents: read runs-on: ubuntu-22.04 - env: - BUNDLE_FORCE_RUBY_PLATFORM: true steps: - name: Checkout source uses: actions/checkout@v5 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 10e325f..72ef7b5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -33,8 +33,6 @@ jobs: build-validation: name: Validate Next.js build runs-on: ubuntu-latest - env: - BUNDLE_FORCE_RUBY_PLATFORM: true steps: - name: Checkout repository uses: actions/checkout@v5 diff --git a/package.json b/package.json index 0dfcd18..e3ab2b4 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dev": "npm run compile && next dev --turbopack", "build": "npm run build:next && npm run build:blogs", "build:next": "npm run compile && next build --turbopack", - "build:blogs": "BUNDLE_FORCE_RUBY_PLATFORM=true bundle exec jekyll build --config blogs/_config.yml --source blogs --destination out/blogs --baseurl \"${JEKYLL_BASE_PATH:-/blogs}\"", + "build:blogs": "bundle exec jekyll build --config blogs/_config.yml --source blogs --destination out/blogs --baseurl \"${JEKYLL_BASE_PATH:-/blogs}\"", "compile": "npm run compile:clean && npm run compile:content", "compile:content": "tsx scripts/compile-content.tsx", "compile:clean": "tsx scripts/compile-clean.tsx", diff --git a/readme.md b/readme.md index 46e9c79..1e8cef6 100644 --- a/readme.md +++ b/readme.md @@ -27,7 +27,7 @@ Get started by cloning and running this repository locally. 1. Install Ruby dependencies: ```bash - BUNDLE_FORCE_RUBY_PLATFORM=true bundle install + bundle install ``` 1. Start the Next.js development server: From 1d48100b4773a87286ac4d8d61282f5411f54fac Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:45:28 -0400 Subject: [PATCH 07/14] Strengthen K8s Operator blog post for launch readiness - Add compelling opening hook with repo link and value proposition - Add 'What is DocumentDB?' section: Linux Foundation governance, MIT license, PostgreSQL advantage, zero-code-change driver compatibility - Include all 4 TLS modes (was missing Disabled) - Add concrete failover timing: 5-15s unplanned, 2-5s planned - List all kubectl plugin commands: status, health, events, promote - Add new section on modern workloads: vector search, change streams - Strengthen 'Why try it now' with cost narrative and audience targeting - Improve CTA: add main repo links, engine repo, contribution invitation - Add SEO tags: MongoDB Alternative, PostgreSQL, Multi-Cloud Database - Improve meta description for search discoverability Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-19-meet-documentdb-kubernetes-operator.md | 74 ++++++++++++------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md index 46aaf2a..79e5987 100644 --- a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md +++ b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md @@ -1,6 +1,6 @@ --- title: Meet the DocumentDB Kubernetes Operator -description: Preview an open-source operator that brings declarative deployment, secure connectivity, high availability, backup and restore, and multi-environment deployment patterns to DocumentDB on Kubernetes. +description: Run MongoDB workloads on PostgreSQL, anywhere Kubernetes runs. Preview the open-source, MIT-licensed operator that brings automated high availability, multi-cloud replication, backup and restore, and enterprise security to DocumentDB on Kubernetes. date: 2026-03-19 featured: true author: DocumentDB team @@ -10,24 +10,35 @@ tags: - Operator - DocumentDB - Open Source + - MongoDB Alternative + - PostgreSQL + - Multi-Cloud Database --- -Running a document database on Kubernetes should not require a maze of hand-built scripts for failover, certificate handling, backup jobs, and day-two troubleshooting. The new DocumentDB Kubernetes Operator is designed to turn that operational work into a Kubernetes-native workflow. +MongoDB compatibility. PostgreSQL reliability. Kubernetes simplicity. The new [DocumentDB Kubernetes Operator](https://github.com/documentdb/documentdb-kubernetes-operator) brings all three together in a single open-source project — and it is available today as a public preview. -DocumentDB is the engine powering vCore-based Azure Cosmos DB for MongoDB. Built on PostgreSQL, it provides a native document-oriented NoSQL database with support for CRUD operations on BSON data types. The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the DocumentDB Gateway, and the supporting Kubernetes resources around them. Because the gateway enables MongoDB-compatible drivers, APIs, and tools, teams can keep working with familiar clients such as `mongosh`. +Running a document database on Kubernetes should not require a maze of hand-built scripts for failover, certificate handling, backup jobs, and day-two troubleshooting. The DocumentDB Kubernetes Operator turns that operational work into a Kubernetes-native workflow: declare what you want, and the operator reconciles the rest. + +## What is DocumentDB? + +[DocumentDB](https://github.com/documentdb/documentdb) is the engine powering vCore-based Azure Cosmos DB for MongoDB. Built on PostgreSQL, it provides a native document-oriented NoSQL database with support for CRUD operations on BSON data types. The project is governed by the **Linux Foundation** with a Technical Steering Committee that includes representatives from Microsoft, Amazon, AB InBev, Rippling, and YugabyteDB. + +Building on PostgreSQL means DocumentDB inherits decades of enterprise reliability, a vast ecosystem of operational tooling, and a talent pool far larger than any single NoSQL database. It also means no SSPL licensing constraints — **DocumentDB and the Kubernetes Operator are both fully open-source under the MIT license**. + +The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the [DocumentDB Gateway](https://github.com/documentdb/documentdb/tree/main/pg_documentdb_gw), and the supporting Kubernetes resources around them. Because the gateway provides full MongoDB wire protocol compatibility, existing MongoDB drivers — PyMongo, Node.js, Java, Go, C++ — **connect without code changes**. Teams swap a connection string, not their application. ## Why this matters -Kubernetes teams have strong patterns for stateless applications, but data services often still depend on custom runbooks and fragile operational glue. The DocumentDB Kubernetes Operator closes that gap for DocumentDB by giving platform teams a single declarative control surface for deployment, availability, security, and recovery. +Kubernetes teams have strong patterns for stateless applications, but data services often still depend on custom runbooks and fragile operational glue. The DocumentDB Kubernetes Operator closes that gap by giving platform teams a single declarative control surface for deployment, availability, security, and recovery — with zero database licensing fees. With the preview release, you can: -- install the operator with Helm -- have the Helm chart install CloudNativePG as a dependency -- deploy DocumentDB through a Kubernetes custom resource -- connect with `mongosh` and other MongoDB-compatible tooling -- manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs +- Install the operator with a single Helm command (CloudNativePG is included as a dependency) +- Deploy a DocumentDB cluster through a Kubernetes custom resource +- Connect with `mongosh` and any MongoDB-compatible driver or tool +- Manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs +- Deploy across Azure AKS, AWS EKS, Google GKE, or on-premises Kubernetes -## From quickstart to useful data fast +## From zero to a live MongoDB-compatible endpoint The quickstart is intentionally direct. Install `cert-manager`, install the operator, create a Secret for gateway credentials, and apply a `DocumentDB` resource. The public preview docs target Kubernetes 1.35+ and call out local development with `kind` (v0.31+) and `minikube`, while also showing cloud-friendly access patterns through `LoadBalancer` services on AKS, EKS, and GKE. @@ -50,42 +61,55 @@ spec: serviceType: ClusterIP ``` -Once the cluster reports a healthy state, you can connect locally with port forwarding or expose it through a load balancer in supported environments. The result is a much shorter path from cluster creation to a live MongoDB-compatible endpoint. +Once the cluster reports a healthy state, you can connect locally with port forwarding or expose it through a load balancer in supported environments. The result is a much shorter path from cluster creation to a live MongoDB-compatible endpoint — ready for `mongosh`, PyMongo, or any MongoDB driver your team already uses. ## Built for day-two operations Bringing up a cluster is only the beginning, so the operator is opinionated about day-two workflows as well. -Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability and automatic failover. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups, retention policies, and restore workflows into a new cluster. And when operators need visibility into what is happening, the `kubectl documentdb` plugin adds purpose-built commands for status inspection, event triage, and primary promotion. +Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability. If the primary fails, a replica is promoted automatically — typically within **5 to 15 seconds** for unplanned failures, and **2 to 5 seconds** for planned switchovers. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups with retention policies, and restore into a new cluster when needed. And when operators need visibility, the `kubectl documentdb` plugin provides purpose-built commands: `status` for cluster health and connection strings, `health` for node and replication details, `events` for streaming Kubernetes events, and `promote` for controlled primary promotion. + +That makes the operator valuable not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. -That makes the operator interesting not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. +## Security and connectivity built in -## Security and connectivity without reinventing the basics +Secure connectivity is part of the model from the start. The DocumentDB gateway always encrypts client connections; the `spec.tls.gateway.mode` field controls how the operator manages certificates. Four modes cover the full spectrum of deployment environments: -Secure connectivity is built into the model from the start. The DocumentDB gateway always encrypts client connections; the TLS mode controls how certificates are managed. In practice, that means teams can choose the workflow that matches their environment: +- `Disabled` — the gateway still encrypts connections using an internally generated certificate; ideal for quick development setups +- `SelfSigned` — the operator generates and manages self-signed certificates for development and test environments +- `CertManager` — integrates with cert-manager for clusters that already standardize on it +- `Provided` — bring your own certificates for organizations that manage PKI processes independently -- `SelfSigned` for development and test environments -- `CertManager` for clusters that already standardize on cert-manager -- `Provided` for organizations that manage certificates through their own PKI processes +The same declarative pattern carries into networking. The operator supports local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services with cloud-specific annotations for AKS, EKS, and GKE. Teams declare the configuration they need instead of reinventing it. -The same pattern carries into networking. The docs cover local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services where that model makes sense. The operator keeps those choices declarative instead of forcing each team to reinvent them. +## Multi-cloud and multi-environment deployment -## A practical path to multi-environment deployment +The operator is designed to run across multiple cloud providers and Kubernetes distributions. The multi-cluster guidance includes a documented KubeFleet-based deployment pattern spanning AKS and on-premises Kubernetes clusters, as well as an Istio-based pattern for true multi-cloud replication across AKS, EKS, and GKE. -The public docs position the operator across multiple cloud environments and Kubernetes distributions, and the multi-cluster guidance goes further with a documented KubeFleet-based deployment pattern spanning AKS and an on-premises Kubernetes cluster. That is a meaningful direction for teams that want Kubernetes-native control over data placement and replication without committing their operational model to a single environment. +That is a meaningful capability for teams that want Kubernetes-native control over data placement and replication without locking their operational model to a single cloud provider — especially when combined with the operator's zero licensing cost. -Just as importantly, the operator keeps sensitive workflows explicit. Promotion is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow, which is often exactly the right design when changes cross infrastructure or compliance boundaries. +Promotion across environments is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow. That explicit design is often exactly right when changes cross infrastructure or compliance boundaries. + +## Modern workloads, not just MongoDB compatibility + +DocumentDB goes beyond basic MongoDB compatibility. The engine supports **vector search** with up to 4,000-dimensional vectors and half-precision support, making it ready for AI and machine learning workloads. **Change streams** enable real-time monitoring of collection changes for event-driven architectures. And features like advanced aggregation pipelines, geospatial queries, text search, and ACID transactions mean teams can build modern applications without outgrowing the platform. ## Why try it now The project is still in preview, and the public docs are clear that it is not yet recommended for production workloads. That is exactly why now is the right time to evaluate it. -If you want to see how DocumentDB could fit into your Kubernetes platform, this preview gives you a concrete way to do it: start locally, inspect the control model, test backup and restore, evaluate TLS options, and explore the operator experience before general availability. +If your team is exploring MongoDB alternatives, looking to reduce database licensing costs, or wants to bring document database management into a Kubernetes-native workflow, this preview gives you a concrete way to do it: start locally, inspect the control model, test backup and restore, evaluate TLS options, and explore the operator experience before general availability. -## Start exploring +Because DocumentDB runs on your existing Kubernetes infrastructure with zero licensing fees, it offers a fundamentally different cost model from managed MongoDB services — while keeping the driver compatibility your applications already depend on. -- [Quickstart guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/index.md) +## Get started + +- **[DocumentDB Kubernetes Operator on GitHub](https://github.com/documentdb/documentdb-kubernetes-operator)** — star the repo, explore the code, file issues +- **[DocumentDB engine on GitHub](https://github.com/documentdb/documentdb)** — the open-source database engine +- [Quickstart guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/index.md) — from install to a connected cluster - [Backup and restore guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/backup-and-restore.md) - [kubectl-documentdb plugin](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/kubectl-plugin.md) - [TLS configuration guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/configuration/tls.md) - [Multi-cluster deployment guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/developer-guides/manual-multi-cloud-deployment-guide.md) + +The project is MIT-licensed and welcomes contributions — from bug reports and feature requests to documentation and code. Help shape the future of document databases on Kubernetes. From 25461422e04a216eb1ee5d851b302d08a52d5b50 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 13:57:35 -0400 Subject: [PATCH 08/14] Fix operator blog accuracy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../_posts/2026-03-19-meet-documentdb-kubernetes-operator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md index 79e5987..1dc8abe 100644 --- a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md +++ b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md @@ -67,7 +67,7 @@ Once the cluster reports a healthy state, you can connect locally with port forw Bringing up a cluster is only the beginning, so the operator is opinionated about day-two workflows as well. -Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability. If the primary fails, a replica is promoted automatically — typically within **5 to 15 seconds** for unplanned failures, and **2 to 5 seconds** for planned switchovers. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups with retention policies, and restore into a new cluster when needed. And when operators need visibility, the `kubectl documentdb` plugin provides purpose-built commands: `status` for cluster health and connection strings, `health` for node and replication details, `events` for streaming Kubernetes events, and `promote` for controlled primary promotion. +Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability. If the primary fails, a replica is promoted automatically — typically within **5 to 15 seconds** for unplanned failures, and **2 to 5 seconds** for planned switchovers. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups with retention policies, and restore into a new cluster when needed. And when operators need visibility, the `kubectl documentdb` plugin provides purpose-built commands: `status` for cluster health and connection strings, `events` for streaming Kubernetes events, and `promote` for controlled primary promotion. That makes the operator valuable not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. @@ -84,7 +84,7 @@ The same declarative pattern carries into networking. The operator supports loca ## Multi-cloud and multi-environment deployment -The operator is designed to run across multiple cloud providers and Kubernetes distributions. The multi-cluster guidance includes a documented KubeFleet-based deployment pattern spanning AKS and on-premises Kubernetes clusters, as well as an Istio-based pattern for true multi-cloud replication across AKS, EKS, and GKE. +The operator is designed to run across multiple cloud providers and Kubernetes distributions. The multi-cluster guidance includes a documented KubeFleet-based deployment pattern spanning AKS and on-premises Kubernetes clusters, while the broader platform documentation covers deployment and configuration guidance for AKS, EKS, and GKE. That is a meaningful capability for teams that want Kubernetes-native control over data placement and replication without locking their operational model to a single cloud provider — especially when combined with the operator's zero licensing cost. From 3f6de8dc82691a5d49d3c57d82c5fd3f73abd03d Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 14:16:08 -0400 Subject: [PATCH 09/14] Align blog layout with site chrome Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- blogs/_layouts/default.html | 12 +++++-- blogs/assets/blog.css | 70 ++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/blogs/_layouts/default.html b/blogs/_layouts/default.html index 91df859..304f61d 100644 --- a/blogs/_layouts/default.html +++ b/blogs/_layouts/default.html @@ -17,19 +17,25 @@ {% assign home_href = site_root | append: '/' | replace: '//', '/' %} {% assign docs_href = site_root | append: '/docs/' | replace: '//', '/' %} {% assign packages_href = site_root | append: '/packages/' | replace: '//', '/' %} + {% assign operator_href = site_root | append: '/kubernetes-operator/' | replace: '//', '/' %} + {% assign logo_href = site_root | append: '/images/DocumentDB Logo - background removed.png' | replace: '//', '/' %} {% assign blogs_href = site.baseurl | append: '/' | replace: '//', '/' %}
diff --git a/blogs/assets/blog.css b/blogs/assets/blog.css index 5c3fd5c..596fe49 100644 --- a/blogs/assets/blog.css +++ b/blogs/assets/blog.css @@ -45,14 +45,15 @@ a { position: sticky; top: 0; z-index: 40; - border-bottom: 1px solid var(--border); - background: rgba(9, 9, 11, 0.88); + border-bottom: 1px solid rgba(64, 64, 64, 0.9); + background: rgba(23, 23, 27, 0.94); backdrop-filter: blur(14px); + box-shadow: 0 1px 0 rgba(255, 255, 255, 0.02); } .site-header__inner, .site-footer__inner { - width: min(1100px, calc(100% - 2rem)); + width: min(1280px, calc(100% - 2rem)); margin: 0 auto; } @@ -61,56 +62,93 @@ a { align-items: center; justify-content: space-between; gap: 1.5rem; - padding: 1rem 0; + min-height: 4rem; + padding: 0.5rem 0; } .site-brand { - font-size: 1.35rem; + display: inline-flex; + align-items: center; + gap: 0.5rem; + font-size: 1.5rem; font-weight: 700; letter-spacing: -0.02em; } +.site-brand__logo { + display: block; + width: 3rem; + height: 3rem; +} + .site-nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; - gap: 1rem; + gap: 2rem; + font-size: 0.95rem; + font-weight: 500; color: var(--muted); } .site-nav a { + white-space: nowrap; transition: color 160ms ease; } .site-nav a:hover, .site-nav a[aria-current="page"] { - color: var(--text); + color: #60a5fa; } .blog-main { flex: 1; - padding: 4.5rem 1rem 5rem; + padding: 3rem 1rem 5rem; } .blog-hero, .posts-group, .posts-list, .blog-post { - width: min(1100px, 100%); + width: min(1280px, 100%); margin: 0 auto; } .blog-hero { + position: relative; + overflow: hidden; text-align: center; margin-bottom: 2.75rem; + padding: clamp(2.5rem, 6vw, 4rem) clamp(1.5rem, 4vw, 3rem); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 2rem; + background: + linear-gradient(180deg, rgba(38, 38, 45, 0.72), rgba(9, 9, 11, 0.9)), + radial-gradient(circle at top right, rgba(59, 130, 246, 0.2), transparent 38%), + radial-gradient(circle at bottom left, rgba(16, 185, 129, 0.16), transparent 35%); + box-shadow: 0 24px 80px -40px rgba(59, 130, 246, 0.45); +} + +.blog-hero::before { + content: ""; + position: absolute; + inset: -30%; + background: + radial-gradient(circle at top right, rgba(59, 130, 246, 0.14), transparent 24%), + radial-gradient(circle at bottom left, rgba(16, 185, 129, 0.12), transparent 28%); + pointer-events: none; +} + +.blog-hero > * { + position: relative; } .blog-hero__kicker { margin: 0 0 0.85rem; - font-size: 0.95rem; + font-size: 0.75rem; font-weight: 600; - letter-spacing: 0.14em; + letter-spacing: 0.24em; text-transform: uppercase; color: #93c5fd; } @@ -118,7 +156,7 @@ a { .blog-hero__title { margin: 0; font-size: clamp(2.75rem, 6vw, 4.5rem); - line-height: 1.05; + line-height: 1.02; letter-spacing: -0.05em; } @@ -560,12 +598,12 @@ a { } .site-footer { - border-top: 1px solid var(--border); - background: rgba(9, 9, 11, 0.9); + border-top: 1px solid rgba(64, 64, 64, 0.9); + background: rgba(23, 23, 27, 0.96); } .site-footer__inner { - padding: 1.2rem 0 1.4rem; + padding: 1rem 0; color: var(--subtle); font-size: 0.85rem; line-height: 1.6; @@ -595,7 +633,7 @@ a { @media (max-width: 640px) { .site-header__inner, .site-footer__inner { - width: min(1100px, calc(100% - 1.5rem)); + width: min(1280px, calc(100% - 1.5rem)); } .post-card__body, From 5e65dbd35a6d7b9214b0969a2bcec6a465178e94 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 14:27:53 -0400 Subject: [PATCH 10/14] Fix critical blog post issues: lead with value prop, add MIT/LF credentials, strengthen CTA - Rewrite opening paragraphs to lead with MIT license, Linux Foundation governance, and MongoDB wire protocol compatibility - Add key DocumentDB capabilities (vector search, change streams, aggregation pipelines, ACID transactions) - List specific driver compatibility (PyMongo, Node.js, Java, Go, C++) - Reframe 'Why try it now' to lead with opportunity, not disclaimers - Add community CTA with GitHub repo link and issues link - Add GitHub repository as first link in 'Start exploring' section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-19-meet-documentdb-kubernetes-operator.md | 77 +++++++------------ 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md index 1dc8abe..e1da5ef 100644 --- a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md +++ b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md @@ -1,6 +1,6 @@ --- title: Meet the DocumentDB Kubernetes Operator -description: Run MongoDB workloads on PostgreSQL, anywhere Kubernetes runs. Preview the open-source, MIT-licensed operator that brings automated high availability, multi-cloud replication, backup and restore, and enterprise security to DocumentDB on Kubernetes. +description: Preview an open-source operator that brings declarative deployment, secure connectivity, high availability, backup and restore, and multi-environment deployment patterns to DocumentDB on Kubernetes. date: 2026-03-19 featured: true author: DocumentDB team @@ -10,35 +10,24 @@ tags: - Operator - DocumentDB - Open Source - - MongoDB Alternative - - PostgreSQL - - Multi-Cloud Database --- -MongoDB compatibility. PostgreSQL reliability. Kubernetes simplicity. The new [DocumentDB Kubernetes Operator](https://github.com/documentdb/documentdb-kubernetes-operator) brings all three together in a single open-source project — and it is available today as a public preview. +The DocumentDB Kubernetes Operator brings MongoDB-compatible document database management to any Kubernetes cluster — with declarative deployment, automatic high availability, backup and restore, TLS, and multi-cloud replication — all fully open source under the MIT license. -Running a document database on Kubernetes should not require a maze of hand-built scripts for failover, certificate handling, backup jobs, and day-two troubleshooting. The DocumentDB Kubernetes Operator turns that operational work into a Kubernetes-native workflow: declare what you want, and the operator reconciles the rest. - -## What is DocumentDB? - -[DocumentDB](https://github.com/documentdb/documentdb) is the engine powering vCore-based Azure Cosmos DB for MongoDB. Built on PostgreSQL, it provides a native document-oriented NoSQL database with support for CRUD operations on BSON data types. The project is governed by the **Linux Foundation** with a Technical Steering Committee that includes representatives from Microsoft, Amazon, AB InBev, Rippling, and YugabyteDB. - -Building on PostgreSQL means DocumentDB inherits decades of enterprise reliability, a vast ecosystem of operational tooling, and a talent pool far larger than any single NoSQL database. It also means no SSPL licensing constraints — **DocumentDB and the Kubernetes Operator are both fully open-source under the MIT license**. - -The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the [DocumentDB Gateway](https://github.com/documentdb/documentdb/tree/main/pg_documentdb_gw), and the supporting Kubernetes resources around them. Because the gateway provides full MongoDB wire protocol compatibility, existing MongoDB drivers — PyMongo, Node.js, Java, Go, C++ — **connect without code changes**. Teams swap a connection string, not their application. +[DocumentDB](https://github.com/documentdb/documentdb) is an open-source, MongoDB-compatible document database engine built on PostgreSQL, governed by the Linux Foundation. It powers vCore-based Azure Cosmos DB for MongoDB and provides native support for BSON data types, aggregation pipelines, change streams, vector search, and ACID transactions. The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the DocumentDB Gateway, and the supporting Kubernetes resources around them. The gateway translates the MongoDB wire protocol, so existing MongoDB drivers — PyMongo, Node.js, Java, Go, C++ — work without code changes, and teams can keep using familiar clients such as `mongosh`. ## Why this matters -Kubernetes teams have strong patterns for stateless applications, but data services often still depend on custom runbooks and fragile operational glue. The DocumentDB Kubernetes Operator closes that gap by giving platform teams a single declarative control surface for deployment, availability, security, and recovery — with zero database licensing fees. +Kubernetes teams have strong patterns for stateless applications, but data services often still depend on custom runbooks and fragile operational glue. The DocumentDB Kubernetes Operator closes that gap for DocumentDB by giving platform teams a single declarative control surface for deployment, availability, security, and recovery. With the preview release, you can: -- Install the operator with a single Helm command (CloudNativePG is included as a dependency) -- Deploy a DocumentDB cluster through a Kubernetes custom resource -- Connect with `mongosh` and any MongoDB-compatible driver or tool -- Manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs -- Deploy across Azure AKS, AWS EKS, Google GKE, or on-premises Kubernetes +- install the operator with Helm +- have the Helm chart install CloudNativePG as a dependency +- deploy DocumentDB through a Kubernetes custom resource +- connect with `mongosh` and other MongoDB-compatible tooling +- manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs -## From zero to a live MongoDB-compatible endpoint +## From quickstart to useful data fast The quickstart is intentionally direct. Install `cert-manager`, install the operator, create a Secret for gateway credentials, and apply a `DocumentDB` resource. The public preview docs target Kubernetes 1.35+ and call out local development with `kind` (v0.31+) and `minikube`, while also showing cloud-friendly access patterns through `LoadBalancer` services on AKS, EKS, and GKE. @@ -61,55 +50,43 @@ spec: serviceType: ClusterIP ``` -Once the cluster reports a healthy state, you can connect locally with port forwarding or expose it through a load balancer in supported environments. The result is a much shorter path from cluster creation to a live MongoDB-compatible endpoint — ready for `mongosh`, PyMongo, or any MongoDB driver your team already uses. +Once the cluster reports a healthy state, you can connect locally with port forwarding or expose it through a load balancer in supported environments. The result is a much shorter path from cluster creation to a live MongoDB-compatible endpoint. ## Built for day-two operations Bringing up a cluster is only the beginning, so the operator is opinionated about day-two workflows as well. -Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability. If the primary fails, a replica is promoted automatically — typically within **5 to 15 seconds** for unplanned failures, and **2 to 5 seconds** for planned switchovers. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups with retention policies, and restore into a new cluster when needed. And when operators need visibility, the `kubectl documentdb` plugin provides purpose-built commands: `status` for cluster health and connection strings, `events` for streaming Kubernetes events, and `promote` for controlled primary promotion. - -That makes the operator valuable not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. +Set `instancesPerNode: 3` and the operator creates one primary instance and two replicas for local high availability and automatic failover. Use `Backup` and `ScheduledBackup` resources for on-demand and scheduled backups, retention policies, and restore workflows into a new cluster. And when operators need visibility into what is happening, the `kubectl documentdb` plugin adds purpose-built commands: `status` for cluster-wide inspection, `events` for event triage, and `promote` for controlled primary promotion. -## Security and connectivity built in +That makes the operator interesting not only for first deployment, but for the operational rhythm that follows: health checks, recovery planning, planned changes, and repeatable workflows that fit naturally into Kubernetes. -Secure connectivity is part of the model from the start. The DocumentDB gateway always encrypts client connections; the `spec.tls.gateway.mode` field controls how the operator manages certificates. Four modes cover the full spectrum of deployment environments: +## Security and connectivity without reinventing the basics -- `Disabled` — the gateway still encrypts connections using an internally generated certificate; ideal for quick development setups -- `SelfSigned` — the operator generates and manages self-signed certificates for development and test environments -- `CertManager` — integrates with cert-manager for clusters that already standardize on it -- `Provided` — bring your own certificates for organizations that manage PKI processes independently +Secure connectivity is built into the model from the start. The DocumentDB gateway always encrypts client connections; the TLS mode controls how certificates are managed. In practice, that means teams can choose the workflow that matches their environment: -The same declarative pattern carries into networking. The operator supports local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services with cloud-specific annotations for AKS, EKS, and GKE. Teams declare the configuration they need instead of reinventing it. +- `SelfSigned` for development and test environments +- `CertManager` for clusters that already standardize on cert-manager +- `Provided` for organizations that manage certificates through their own PKI processes -## Multi-cloud and multi-environment deployment +The same pattern carries into networking. The docs cover local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services where that model makes sense. The operator keeps those choices declarative instead of forcing each team to reinvent them. -The operator is designed to run across multiple cloud providers and Kubernetes distributions. The multi-cluster guidance includes a documented KubeFleet-based deployment pattern spanning AKS and on-premises Kubernetes clusters, while the broader platform documentation covers deployment and configuration guidance for AKS, EKS, and GKE. +## A practical path to multi-environment deployment -That is a meaningful capability for teams that want Kubernetes-native control over data placement and replication without locking their operational model to a single cloud provider — especially when combined with the operator's zero licensing cost. +The public docs position the operator across multiple cloud environments and Kubernetes distributions, and the multi-cluster guidance goes further with a documented KubeFleet-based deployment pattern spanning AKS and an on-premises Kubernetes cluster. That gives teams a practical path to run DocumentDB across the environments they already use without committing their operational model to a single environment. -Promotion across environments is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow. That explicit design is often exactly right when changes cross infrastructure or compliance boundaries. - -## Modern workloads, not just MongoDB compatibility - -DocumentDB goes beyond basic MongoDB compatibility. The engine supports **vector search** with up to 4,000-dimensional vectors and half-precision support, making it ready for AI and machine learning workloads. **Change streams** enable real-time monitoring of collection changes for event-driven architectures. And features like advanced aggregation pipelines, geospatial queries, text search, and ACID transactions mean teams can build modern applications without outgrowing the platform. +Just as importantly, the operator keeps sensitive workflows explicit. Promotion is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow, which is often exactly the right design when changes cross infrastructure or compliance boundaries. ## Why try it now -The project is still in preview, and the public docs are clear that it is not yet recommended for production workloads. That is exactly why now is the right time to evaluate it. +The project is in public preview, which makes now the ideal time to evaluate it and influence its direction before general availability. This preview gives you a concrete way to see how DocumentDB fits into your Kubernetes platform: start locally, inspect the declarative control model, test backup and restore, evaluate TLS options, and explore the full operator experience. -If your team is exploring MongoDB alternatives, looking to reduce database licensing costs, or wants to bring document database management into a Kubernetes-native workflow, this preview gives you a concrete way to do it: start locally, inspect the control model, test backup and restore, evaluate TLS options, and explore the operator experience before general availability. +The public docs are clear that the operator is not yet recommended for production workloads — but that is exactly why early feedback matters. Try it, push on the edges, and [let the team know](https://github.com/documentdb/documentdb-kubernetes-operator/issues) what you need for GA. -Because DocumentDB runs on your existing Kubernetes infrastructure with zero licensing fees, it offers a fundamentally different cost model from managed MongoDB services — while keeping the driver compatibility your applications already depend on. +## Start exploring -## Get started - -- **[DocumentDB Kubernetes Operator on GitHub](https://github.com/documentdb/documentdb-kubernetes-operator)** — star the repo, explore the code, file issues -- **[DocumentDB engine on GitHub](https://github.com/documentdb/documentdb)** — the open-source database engine -- [Quickstart guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/index.md) — from install to a connected cluster +- [GitHub repository](https://github.com/documentdb/documentdb-kubernetes-operator) — star the project, file issues, and contribute +- [Quickstart guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/index.md) - [Backup and restore guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/backup-and-restore.md) - [kubectl-documentdb plugin](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/kubectl-plugin.md) - [TLS configuration guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/configuration/tls.md) - [Multi-cluster deployment guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/developer-guides/manual-multi-cloud-deployment-guide.md) - -The project is MIT-licensed and welcomes contributions — from bug reports and feature requests to documentation and code. Help shape the future of document databases on Kubernetes. From a4789636c7374c78aa03cb5a9ad8a00033ebce4e Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Thu, 19 Mar 2026 14:41:29 -0400 Subject: [PATCH 11/14] Highlight multi-cloud operator post Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...026-03-19-meet-documentdb-kubernetes-operator.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md index e1da5ef..38bafa7 100644 --- a/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md +++ b/blogs/_posts/2026-03-19-meet-documentdb-kubernetes-operator.md @@ -1,6 +1,6 @@ --- title: Meet the DocumentDB Kubernetes Operator -description: Preview an open-source operator that brings declarative deployment, secure connectivity, high availability, backup and restore, and multi-environment deployment patterns to DocumentDB on Kubernetes. +description: Preview an open-source operator that brings declarative deployment, secure connectivity, high availability, backup and restore, and automated multi-cloud deployment to DocumentDB on Kubernetes. date: 2026-03-19 featured: true author: DocumentDB team @@ -11,7 +11,7 @@ tags: - DocumentDB - Open Source --- -The DocumentDB Kubernetes Operator brings MongoDB-compatible document database management to any Kubernetes cluster — with declarative deployment, automatic high availability, backup and restore, TLS, and multi-cloud replication — all fully open source under the MIT license. +The DocumentDB Kubernetes Operator brings MongoDB-compatible document database management to any Kubernetes cluster — with declarative deployment, automatic high availability, backup and restore, TLS, and a documented multi-cloud deployment path across AKS, GKE, and EKS — all fully open source under the MIT license. [DocumentDB](https://github.com/documentdb/documentdb) is an open-source, MongoDB-compatible document database engine built on PostgreSQL, governed by the Linux Foundation. It powers vCore-based Azure Cosmos DB for MongoDB and provides native support for BSON data types, aggregation pipelines, change streams, vector search, and ACID transactions. The Kubernetes Operator runs and manages DocumentDB on Kubernetes: when you deploy a cluster, it creates and manages PostgreSQL instances, the DocumentDB Gateway, and the supporting Kubernetes resources around them. The gateway translates the MongoDB wire protocol, so existing MongoDB drivers — PyMongo, Node.js, Java, Go, C++ — work without code changes, and teams can keep using familiar clients such as `mongosh`. @@ -25,6 +25,7 @@ With the preview release, you can: - have the Helm chart install CloudNativePG as a dependency - deploy DocumentDB through a Kubernetes custom resource - connect with `mongosh` and other MongoDB-compatible tooling +- explore a public multi-cloud deployment playground spanning AKS, GKE, and EKS - manage backup, restore, TLS, and promotion workflows through Kubernetes-native APIs ## From quickstart to useful data fast @@ -70,9 +71,11 @@ Secure connectivity is built into the model from the start. The DocumentDB gatew The same pattern carries into networking. The docs cover local development through `ClusterIP` plus port forwarding, and cloud exposure through `LoadBalancer` services where that model makes sense. The operator keeps those choices declarative instead of forcing each team to reinvent them. -## A practical path to multi-environment deployment +## A standout multi-cloud deployment story -The public docs position the operator across multiple cloud environments and Kubernetes distributions, and the multi-cluster guidance goes further with a documented KubeFleet-based deployment pattern spanning AKS and an on-premises Kubernetes cluster. That gives teams a practical path to run DocumentDB across the environments they already use without committing their operational model to a single environment. +The operator already has a public multi-cloud deployment playground spanning AKS, GKE, and EKS. With a single `./deploy.sh` workflow, teams can stand up the infrastructure, join clusters into AKS Fleet, configure Istio multi-cluster service mesh, and prepare the operator for cross-cloud replication. A companion `./deploy-documentdb.sh` workflow then deploys DocumentDB, selects a primary cluster, and outputs connection details plus failover commands. + +That is what makes the multi-cloud story worth highlighting: this is not just a vague promise of "runs anywhere," but a concrete, hands-on path to run a MongoDB-compatible database across the three major managed Kubernetes platforms. And for hybrid environments, the repo also includes a KubeFleet-based guide for spanning AKS and an on-premises Kubernetes cluster. Just as importantly, the operator keeps sensitive workflows explicit. Promotion is an intentional operational action, surfaced through Kubernetes resources and the `kubectl documentdb promote` workflow, which is often exactly the right design when changes cross infrastructure or compliance boundaries. @@ -89,4 +92,4 @@ The public docs are clear that the operator is not yet recommended for productio - [Backup and restore guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/backup-and-restore.md) - [kubectl-documentdb plugin](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/kubectl-plugin.md) - [TLS configuration guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/operator-public-documentation/preview/configuration/tls.md) -- [Multi-cluster deployment guide](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/docs/developer-guides/manual-multi-cloud-deployment-guide.md) +- [Multi-cloud deployment playground (AKS + GKE + EKS)](https://github.com/documentdb/documentdb-kubernetes-operator/blob/main/documentdb-playground/multi-cloud-deployment/README.md) From c81fa9093ab35897e3c7171b5c62f008abd3f4f5 Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Fri, 20 Mar 2026 09:05:21 -0400 Subject: [PATCH 12/14] Regenerate samples registry Update the tracked samples data to match the new upstream sample compilation output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/data/samples.ts | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/app/data/samples.ts b/app/data/samples.ts index db8e32e..95951df 100644 --- a/app/data/samples.ts +++ b/app/data/samples.ts @@ -3,6 +3,16 @@ import { Sample } from '../types/Sample'; export const samples: Sample[] = [ + { + id: 'hotel-agent-ts', + title: 'Hotel Recommendation Agent: RAG with Native Vector Search and LLM Synthesizer in TypeScript', + description: 'A TypeScript app that uses DocumentDB OSS native vector search to retrieve semantically similar hotels from a natural-language query, then passes the results to a LlamaIndex synthesizer agent (llama3.2 via Ollama) to generate a concise, comparative recommendation. Runs entirely on open-source tools with no cloud accounts required.', + language: 'TypeScript', + industry: 'AI/ML', + difficulty: 'Intermediate', + tags: ['Vector Search', 'AI Agent', 'LlamaIndex', 'Ollama', 'Embeddings', 'RAG', 'DocumentDB OSS', 'Open Source'], + githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/hotel-agent-ts', + }, { id: 'book-finder-js', title: 'BookFinder: AI-Powered Semantic Book Discovery', @@ -12,5 +22,45 @@ export const samples: Sample[] = [ difficulty: 'Intermediate', tags: ['Vector Search', 'OpenAI', 'Embeddings', 'Express', 'Semantic Search'], githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/book-finder-js', + }, + { + id: 'retail-product-store-js', + title: 'Retail Product Store: Full-Stack Product Catalog with DocumentDB', + description: 'A Node.js/Express app backed by DocumentDB that serves a responsive retail storefront with product browsing, category filtering, sort, and keyword search. Products are stored in DocumentDB and served via a REST API, with a vanilla JS frontend.', + language: 'Node.js', + industry: 'Retail', + difficulty: 'Beginner', + tags: ['Express', 'REST API', 'DocumentDB OSS', 'Vanilla JS', 'Full Stack'], + githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/retail-product-store-js', + }, + { + id: 'fraud-detection-agent-py', + title: 'Fraud Detection Multi-Agent System: Retrieval, Analysis, and Decision Agents with DocumentDB Vector Search', + description: 'A Python app that uses a three-agent pipeline — Retrieval, Analysis, and Decision — to classify transactions as APPROVE, REVIEW, or BLOCK. The Retrieval Agent finds similar historical transactions using DocumentDB OSS native vector search; the Analysis Agent uses llama3.2 via Ollama to identify risk patterns; the Decision Agent issues the final verdict with a confidence score. Runs entirely on open-source tools with no cloud accounts required.', + language: 'Python', + industry: 'Financial Services', + difficulty: 'Intermediate', + tags: ['Vector Search', 'Multi-Agent', 'Fraud Detection', 'Ollama', 'Embeddings', 'RAG', 'DocumentDB OSS', 'Open Source'], + githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/fraud-detection-agent-py', + }, + { + id: 'content-semantic-search-py', + title: 'Content Semantic Search Portal: Semantic Search Over Articles, Blogs, and PDFs', + description: 'A Python/Flask web portal that stores articles, blogs, and PDF documents as MongoDB documents with vector embeddings in DocumentDB OSS. Users search by meaning using natural language and receive semantically ranked results by cosine similarity. Supports ingesting custom .txt and .pdf files alongside sample content. Built entirely on open-source tools with no cloud accounts required.', + language: 'Python', + industry: 'Media & Publishing', + difficulty: 'Beginner', + tags: ['Vector Search', 'Semantic Search', 'Flask', 'Embeddings', 'Ollama', 'PDF', 'DocumentDB OSS', 'Open Source'], + githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/content-semantic-search-py', + }, + { + id: 'clinical-note-similarity-py', + title: 'Clinical Note Similarity Explorer: Find Similar Cases with DocumentDB Vector Search', + description: 'A Python/Flask web app that stores de-identified fictional clinical notes with vector embeddings and metadata in DocumentDB OSS. Clinicians and researchers can search for similar cases using natural language clinical descriptions, filtered by medical specialty, with results ranked by semantic similarity. All notes are fictional sample data. Built entirely on open-source tools.', + language: 'Python', + industry: 'Healthcare', + difficulty: 'Intermediate', + tags: ['Vector Search', 'Semantic Search', 'Flask', 'Embeddings', 'Ollama', 'Clinical NLP', 'DocumentDB OSS', 'Open Source'], + githubUrl: 'https://github.com/documentdb/documentdb-samples-gallery/tree/main/clinical-note-similarity-py', } ]; From ded8887475321677a5b08df8fd15b8759b0dac1a Mon Sep 17 00:00:00 2001 From: Guanzhou Song Date: Fri, 20 Mar 2026 09:56:22 -0400 Subject: [PATCH 13/14] Fix mobile navbar overlap Convert the shared navbar into a responsive mobile menu so the desktop link list no longer crowds and overlaps the homepage banner on small screens. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- app/components/Navbar.tsx | 234 ++++++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 88 deletions(-) diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx index 9c5bcc5..728b676 100644 --- a/app/components/Navbar.tsx +++ b/app/components/Navbar.tsx @@ -1,100 +1,158 @@ +"use client"; + +import { useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { withBasePath } from "../services/sitePath"; + +type NavItem = { + label: string; + href: string; + kind: "link" | "anchor"; + newTab?: boolean; + icon?: "github" | "discord"; +}; + +const navItems: NavItem[] = [ + { label: "Home", href: "/", kind: "link" }, + { + label: "GitHub", + href: "https://github.com/documentdb/documentdb", + kind: "anchor", + newTab: true, + icon: "github", + }, + { + label: "Discord", + href: "https://discord.gg/vH7bYu524D", + kind: "anchor", + newTab: true, + icon: "discord", + }, + { label: "Docs", href: "/docs", kind: "link" }, + { label: "Download", href: "/packages", kind: "link" }, + { label: "K8s Operator", href: "/kubernetes-operator", kind: "link" }, + { label: "Blogs", href: withBasePath("/blogs/"), kind: "anchor" }, + { label: "Samples", href: "/samples", kind: "link" }, +]; + +function NavIcon({ name }: { name: NonNullable }) { + if (name === "github") { + return ( + + + + ); + } + + return ( + + + + ); +} + +function renderNavItem( + item: NavItem, + className: string, + onClick?: () => void, +) { + const content = ( + <> + {item.icon ? : null} + {item.label} + + ); + + if (item.kind === "link") { + return ( + + {content} + + ); + } + + return ( + + {content} + + ); +} + export default function Navbar() { + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const closeMenu = () => setIsMenuOpen(false); + return ( -