The static site for DDEV, built with Astro and Tailwind CSS, and hosted on Cloudflare Pages.
Contributor Training · Markdown Formatting · Agent Guidance · Sponsor DDEV
The layout follows Astro’s project structure. The parts specific to this site:
cache/– GitHub API responses cached during local development, to reduce API calls.public/– images, logos, and redirects, copied as they are intodist/.src/content/– Markdown for the blog posts and authors, validated by the content collections schemas insrc/content.config.ts.src/pages/–.astroand.mdxpages whose filenames become routes.src/layouts/–Layout.astro, used by every page, andMarkdownLayout.astro, used by the.mdxpages.src/lib/– GitHub API fetching, the search index, and the remark and rehype plugins for blog Markdown.src/featured-sponsors.json– the featured sponsors, see Sponsor Management.
DDEV includes all the dependencies.
- Run
ddev start. It installs dependencies and starts the dev server. - Open
https://<projectname>.ddev.site:4321. The dev server reloads as you edit.
| Command | Action |
|---|---|
ddev npm run build |
Build the production site to dist/, served at https://<projectname>.ddev.site |
ddev prettier [file] |
Fix formatting of the whole tree, or of the given files |
ddev textlint [file] |
Fix wording in src/content/**, or in the given files, then report what is left |
ddev logs |
Dev server output, for troubleshooting |
Run ddev prettier and ddev textlint before committing. CI runs the same checks.
- Run
nvm useto use the Node.js version in.nvmrc. - Run
npm install. - Run
npm run dev, and openhttp://localhost:4321/. If it fails, runnpm cache clean --force && npm install && npm run dev.
npm run build builds to dist/, and npm run preview serves the build. npm run prettier:fix and npm run textlint:fix && npm run textlint replace the DDEV commands above.
When switching from this setup to DDEV, delete node_modules/ and run ddev npm install, since the two architectures can conflict.
Not needed to contribute a blog post. Contributors, sponsors, releases, and other DDEV data come from the GitHub API; without a token, sponsorship data falls back to sample data. To use the real data:
- Run
cp .env.example .env. Don’t commit.env. - Create a classic GitHub access token with the scopes
repo,read:org,read:user, andread:project. - Paste the token after
GITHUB_TOKEN=in.env.
.editorconfig and .prettierrc hold the formatting rules. VS Code suggests the extensions in .vscode/extensions.json (Prettier, EditorConfig, Astro), and .vscode/settings.json formats on save.
Blog posts are Markdown files in src/content/blog/, named with a kebab-case slug, for example my-new-post.md. For callouts, code blocks, images, and other features, see MARKDOWN_FORMATTING.md. Use this frontmatter:
---
title: "It’s A Post!"
pubDate: 2026-01-01
modifiedDate: 2026-01-03
modifiedComment: "This got updated"
summary:
author: Randy Fay
featureImage:
src: /img/blog/2026/01/kebab-case.jpg
srcDark:
alt:
caption:
credit:
categories:
- DevOps
---authormust match thenameof an author insrc/content/authors/. Add one there for a new author.- Write descriptive
alttext for the feature image.captionandcreditcan use Markdown, wrapped in straight quotes ("). - Choose categories from
allowedCategoriesinsrc/content.config.ts. The first one shows on post cards: Add-ons, Announcements (releases, organization news), Community (events, third-party developments), DevOps (workflows, infrastructure), Performance (benchmarks, tips), Guides (how-to posts), Newsletters, TechNotes (code-level discussions), Training (contributor training), Videos. - Put images in
public/img/blog/YYYY/MM/. The build converts PNG, JPEG, and GIF images to WebP, but the source files are committed as they are, so keep them under 2MB and no wider than about 2000px. ImageOptim applies lossless compression.
Blog comments use giscus.
Add a .astro or .mdx file to src/pages/, and its name becomes the URL. Reuse the layout and components of an existing page. For generated pages, see src/pages/blog/[page].astro, src/pages/blog/category/[slug].astro, and src/pages/blog/author/[id].astro.
.textlintrc checks src/content/** for terminology and stop words, using textlint’s default terminology with a few overrides, such as allowing “website”, “front end”, and “command line”.
src/featured-sponsors.json lists the featured sponsors shown on the home page and in the light and dark badges generated for the main DDEV README. To add one, follow .claude/skills/add-sponsor/SKILL.md, or ask Claude Code to add the sponsor’s website.
Add redirects to public/_redirects. They can point to pages on the site, the DDEV docs, or external resources.
- Most redirects should be
301, a permanent redirect. - Prefix short links with
/s, for example/s/port-conflict.
- GitHub Actions runs the test workflow on every push to
mainand every pull request. - Cloudflare Pages runs
npm run buildon every push tomainand deploysdist/. It also builds a preview for each branch and comments the URL on its PR. Pull requests from forks get previews from GitHub Actions instead, see FORK_PREVIEW_SETUP.md.
The site uses Octokit for GitHub REST and GraphQL requests, which need a token to authenticate and to stay within quota. GitHub Actions supplies its own GITHUB_TOKEN. Anywhere else, including local development and Cloudflare, set GITHUB_TOKEN to a classic personal access token with the scopes listed under GitHub Token.