Skip to content

Set up Cloudflare Pages deployment for the website and docs apps #6472

@luizhf42

Description

@luizhf42

Tracks deploying the website and docs static apps separately via Cloudflare Pages. Moved out of ui/apps/website/DEPLOY-PLAN.md, which was an internal planning note removed from the repo in #6471. Paths and the production branch have been corrected to the current monorepo layout (ui/..., master).

Context

The website (site + blog) and docs are static apps inside the ShellHub monorepo. Today everything runs via Docker Compose alongside the main app. The goal is to deploy the static site separately via Cloudflare Pages, independent of the product's release cycle so that publishing a blog post does not rebuild the app and shipping a new app version does not rebuild the site.

Architecture

shellhub/ (monorepo)
│
├── ui/apps/website   → Cloudflare Pages (shellhub.io)
├── ui/apps/docs      → Cloudflare Pages (docs.shellhub.io)
│
├── api/             ─┐
├── ssh/              │→ Docker Compose (app.shellhub.io)
├── gateway/          │   Separate deploy via release tags
├── ui/              ─┘

Cloudflare Pages configuration

Project 1: shellhub-website

Field Value
Project name shellhub-website
Repository shellhub-io/shellhub
Production branch master
Root directory ui
Build command npm run build --workspace=apps/website
Output directory apps/website/dist
Custom domain shellhub.io
Node version (env var) NODE_VERSION=22

Project 2: shellhub-docs

Field Value
Project name shellhub-docs
Repository shellhub-io/shellhub
Production branch master
Root directory ui
Build command npm run build --workspace=apps/docs
Output directory apps/docs/dist
Custom domain docs.shellhub.io
Node version (env var) NODE_VERSION=22

Deploy flow

Blog post / site content

A writer creates a post.mdx in the repo and opens a PR; Cloudflare generates an automatic preview deploy (a temporary URL like abc123.shellhub-website.pages.dev). After content review on the preview, the PR is merged to master; Cloudflare detects the push, builds apps/website, and deploys in ~30-60s, going live at shellhub.io/blog/<slug>.

Docs

Same flow. Changes under apps/docs/ trigger a rebuild of the shellhub-docs project.

Main app (API, SSH, Gateway, UI)

Independent flow — stays on Docker Compose / release tags as today. Cloudflare Pages is unaffected.

Blog — content structure

The blog uses MDX files in the repository:

ui/apps/website/
├── src/
│   ├── pages/
│   │   └── blog/
│   │       ├── index.astro          # Post listing
│   │       └── [slug].astro         # Individual post
│   ├── layouts/
│   │   └── BlogLayout.astro         # Post layout
│   └── components/
│       └── blog/                    # Blog components
├── content/
│   └── blog/                        # MDX posts
└── astro.config.ts                  # Content collections config

Post frontmatter

---
title: "Post Title"
description: "Brief description for SEO and cards"
author: "username"
date: 2026-02-13
categories:
  - product
tags:
  - ssh
  - security
image: "./cover.png"
draft: false
---

Astro content collections

Astro natively supports content collections with Zod schema validation:

// src/content.config.ts
import { defineCollection, z } from "astro:content";

const blog = defineCollection({
  type: "content",
  schema: z.object({
    title: z.string(),
    description: z.string(),
    author: z.string(),
    date: z.date(),
    categories: z.array(z.string()).optional(),
    tags: z.array(z.string()).optional(),
    image: z.string().optional(),
    draft: z.boolean().default(false),
  }),
});

export const collections = { blog };

Considerations

  • Monorepo build: Cloudflare Pages runs npm install from the configured root directory (ui). Since we use npm workspaces, the install resolves dependencies for all apps; the build command with --workspace=apps/website builds only the desired app.
  • Environment variables: configure NODE_VERSION=22 and any required VITE_SHELLHUB_* (e.g. VITE_SHELLHUB_CLOUD=true) in the Cloudflare Pages dashboard.
  • Preview deploys: each PR automatically generates a preview deploy with a unique URL — useful for reviewing blog posts and site changes before merging.
  • Rollback: Cloudflare keeps deploy history; instant rollback to any previous version via the dashboard.
  • Free tier covers this: unlimited builds and bandwidth, 500 deploys/month, automatic per-PR preview deploys, global CDN with edge caching, custom domains with automatic SSL, and monorepo support.

TODO

  • Set up the shellhub-website project on Cloudflare Pages
  • Set up the shellhub-docs project on Cloudflare Pages
  • Configure custom domains and DNS
  • Implement the blog in the website app (Astro content collections)
  • Create the blog layout (listing + individual post)
  • Migrate existing content from the current blog (if any)
  • Test preview deploys with a test PR
  • Configure redirects (if needed, via a _redirects file)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions