Document Specifications
Authors
Public Release
Overview
Welcome to the official YPYM documentation for Stateless Ghost CMS Architecture: Solving Persistent Image Loss and Speed Penalties via Cloudflare R2. This guide outlines the core utility, technical specifications, and key features of this component within the YPYM MarTech ecosystem. When building high-performance digital ecosystems, one of the most critical challenges is balancing infrastructure reliability with page speed optimization. At YPYM (Your Page Your Money), we rely on Ghost CMS as the main engine for our editorial publication channels: the science article and blog feed Ink & Thought, and our corporate Company Press Release channel. However, Ghost CMS’s out-of-the-box configuration in containerized environments (Docker/Coolify) carries severe structural limitations. This case study details how we eliminated post-deployment image loss, optimized Core Web Vitals page speed metrics, bypassed custom S3 API configuration challenges, and resolved strict browser security policies.1. The Core Issue: Stateless Container Lifecycles vs. Volatile Local Storage
By default, Ghost CMS stores all uploaded editorial images in the container’s local directory:/var/lib/ghost/content/images/. This pattern is highly problematic when deployed inside containerized cloud platforms (such as Docker, Coolify, or Nixpacks).
🚨 THE STATELESS LIFECYCLE ISSUE
Docker containers are fundamentally ephemeral. Every time we deploy code updates, perform server maintenance, or trigger automatic container rebuilds in Coolify, the old container instance is destroyed and replaced with a fresh build. Consequently, all uploaded cover images and assets are wiped instantly, triggering 404 errors (broken images) for visitors.
Additionally, serving high-resolution images directly from the Node.js/Ghost CMS application server incurs unnecessary computation overhead. Every image request triggers disk I/O, spikes Time to First Byte (TTFB), and exhausts resources that the core app server should dedicate to rendering dynamic content. This directly degrades Core Web Vitals, particularly the Largest Contentful Paint (LCP) metric.
2. The Solution Architecture: Cloudflare R2 & Custom CDN
To solve these problems permanently, we decoupled the media storage tier from the application layer. We selected Cloudflare R2 Object Storage as our media backend. Unlike traditional cloud object storage providers, Cloudflare R2 guarantees Zero Egress Fees (no data transfer charges for downloads). Since blog assets are fetched thousands of times by global visitors, offloading images to R2 avoids the unpredictable and compounding network costs of conventional S3 providers. Now, when an editor uploads an image via the Ghost Admin panel (ghost.ypym.app/ghost), the storage adapter intercepts the operation and uploads the file directly to our R2 bucket, named ypym-blog-images. To deliver these assets, we configured a custom CDN subdomain: https://cdn.ypym.app.
// New Asset Delivery Pipeline
User Browser -> Request Image -> cdn.ypym.app (Cloudflare Edge Cache)
-> MISS -> Cloudflare R2 Bucket (ypym-blog-images)
-> HIT -> Served instantly from the nearest Edge Cache (<30ms)
3. Technical Challenges & Engineering Resolutions
While the architectural blueprint is straightforward, implementing it in production under Coolify introduced several low-level technical challenges that required custom resolutions:Challenge A: Dependency Flattening & Volume Mount Conflicts
Initially, we attempted to install theghost-storage-adapter-s3 dependency manually using standard container terminal access. However, npm flattens dependencies into /var/lib/ghost/current/node_modules/ or temporary folders. Because these directories sit outside our persistent mount (/var/lib/ghost/content/), the adapter files were completely wiped on rebuilds, causing Cannot find module runtime failures and launching the container into a fatal boot loop.
✔️ Resolution: We wrote an automation script within the container’s custom entrypoint in Coolify. On boot, the script checks if the persistent directory /var/lib/ghost/content/adapters/storage/s3 is present. If missing, it initializes a local npm scope inside the persistent volume, installs the S3 adapter package and its dependencies, writes a custom index.js initialization entrypoint, and configures matching permissions. The stack is now fully persistent and self-healing.
Challenge B: Cloudflare R2 Virtual-Hosted Bucket Naming
By default, the AWS S3 SDK constructs bucket endpoints using virtual-hosted naming conventions (e.g.https://ypym-blog-images.<account-id>.r2.cloudflarestorage.com). This pattern is rejected by Cloudflare R2, leading to DNS lookup issues and SSL connection handshakes failures.
✔️ Resolution: We injected the environment variable storage__s3__forcePathStyle=true into the service. This forces the SDK client to route commands using path-style URIs (e.g. https://<account-id>.r2.cloudflarestorage.com/ypym-blog-images/), which are fully supported by Cloudflare R2.
Challenge C: Content Security Policy (CSP) Blocking
Once R2 S3 uploads were successfully working, we noticed visitor browsers still failed to load cover images, showing broken links instead. Reviewing the web console logs revealed a strict security block: Content Security Policy (CSP) directive violation. The Astro frontend middleware was blocking images from loading because our newhttps://cdn.ypym.app domain was not whitelisted.
✔️ Resolution: We modified the Astro server security filter in middleware.ts to whitelist https://cdn.ypym.app within the img-src and media-src directives. Pushing this change and redeploying the site allowed browsers to render the S3/R2 assets securely.
4. Impact on YPYM’s SEO & Digital Strategy
Offloading image storage directly supports our long-term SEO and Brand Experience guidelines:-
Core Web Vitals & LCP Speed: Moving static assets to the Cloudflare Edge network (
cdn.ypym.app) frees the main site application server from delivering large files. Cover image latency decreased from a 1.2-second average to under 150ms globally. This improves the Largest Contentful Paint (LCP) score and overall performance metrics. -
Brand Equity & Google Image Indexing: By using a branded domain (
cdn.ypym.app) instead of generic cloud endpoints, all assets within Ink & Thought and our Company Press Release channels are indexed directly under our own domain authority, building long-term link integrity and image search authority. - Zero-Downtime Siaran Pers (Press Releases): Our corporate communications are now highly reliable. Infrastructure reboots and server migrations will never trigger broken images on historical press statements, ensuring a premium and consistent brand image for investors and partners. “
Next Steps & Directory
- Product Directory: Explore other sub-platforms and services in the Product Directory.
- Overview Main: Return to the Ecosystem Overview for general repository listings.
- Documentation Hub: Visit the YPYM Hub for developer setup files and brand rules.
- Main Website: Reach out to the team at the Official Website.