Run WordPress on Google Cloud Run — no MySQL, no persistent disk, no fuss.
This project combines SQLite, Litestream, and GCS FUSE to run WordPress as a truly stateless container. Everything is managed through Composer, deployed with a single script, and designed to be secure, low-cost, and low-maintenance.
- 🚫 No MySQL — Uses the official SQLite Database Integration plugin
- 📦 Composer-managed WordPress — Core, plugins, and themes are all declared in
composer.json - 🪣 GCS FUSE for media — Uploaded files are stored in a GCS bucket, not on local disk
- 🔄 Litestream for durability — SQLite is continuously replicated to GCS, restored on cold start
- ⚡ FrankenPHP — Fast cold-start spin-up, ideal for serverless containers
- 🏗️ Single-command deploy — One script builds images and deploys to Cloud Run
- 💰 Cost-effective — No always-on database servers; pay only for what you use
┌─────────────────────── Cloud Run Service ───────────────────────┐
│ │
│ ┌──────────────┐ shared volume ┌───────────────────┐ │
│ │ WordPress │◄──── (in-memory) ───►│ Litestream │ │
│ │ FrankenPHP │ /data/db.sqlite │ (sidecar) │ │
│ │ + SQLite │ │ restore → watch │ │
│ └──────┬───────┘ └─────────┬─────────┘ │
│ │ │ │
└─────────┼─────────────────────────────────────────┼──────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ GCS Bucket │ │ GCS Bucket │
│ (media) │ │ (db backup) │
└─────────────┘ └─────────────┘
How it works:
- On container startup, the Litestream sidecar restores the latest SQLite snapshot from GCS.
- Once the database is ready, the WordPress container starts serving requests via FrankenPHP.
- New uploads are written directly to GCS through GCS FUSE — no local storage needed.
- Litestream continuously replicates SQLite changes to GCS, so the container stays stateless.
- Google Cloud SDK (
gcloud) - A GCP project with billing enabled
- Two GCS buckets — one for media uploads, one for database backups
- Docker (for local development)
├── composer.json # WordPress core + plugins + themes
├── wp-config.php # WordPress configuration (reads env vars)
├── Dockerfile # Multi-stage: Composer build → FrankenPHP
├── docker-compose.yml # Local development environment
├── .env.example # Template for environment variables
├── themes/ # Custom themes go here
└── cloudrun/
├── deploy.sh # One-command build & deploy script
├── cloudbuild.yaml # Cloud Build pipeline definition
├── service.yaml # Cloud Run service template (Knative)
├── Dockerfile.litestream # Litestream sidecar image
├── litestream.yml # Litestream replication config
└── start-litestream.sh # Entrypoint: restore then replicate
Run WordPress locally with Docker Compose:
# Edit docker-compose.yml — for local dev, only AUTH_KEY…NONCE_SALT are needed
docker compose upVisit http://localhost:8080 to complete the WordPress setup wizard.
Note: Locally, SQLite data is stored in
local-wp-data/and GCS FUSE is not used.
cp .env.example .envEdit .env with your GCP project details:
| Variable | Description |
|---|---|
PROJECT_ID |
Your GCP project ID |
MEDIA_BUCKET |
GCS bucket name for media uploads |
DB_BUCKET |
GCS bucket name for SQLite backups |
WP_HOME |
Your site URL (e.g., https://example.com) |
source .env
# Create Artifact Registry repository (one-time)
gcloud artifacts repositories create "$REPOSITORY" \
--repository-format=docker \
--location="$REGION" \
--project="$PROJECT_ID"
# Create GCS buckets (one-time)
gcloud storage buckets create "gs://$MEDIA_BUCKET" --project="$PROJECT_ID" --location="$REGION"
gcloud storage buckets create "gs://$DB_BUCKET" --project="$PROJECT_ID" --location="$REGION"bash cloudrun/deploy.shThe deploy script will:
- Load your
.envconfiguration - Generate WordPress auth salts (or reuse cached salts from
cloudrun/wp-salts.env) - Build the WordPress and Litestream container images via Cloud Build
- Deploy the service to Cloud Run using the Knative template
Safety: The script validates that all placeholder values have been replaced before deploying. Running with default
.env.examplevalues will exit with an error.
Add it to composer.json via WordPress Packagist:
composer require wpackagist-plugin/your-plugin-namecomposer require wpackagist-theme/your-theme-nameOr place custom theme files in the themes/ directory — they are copied into the image at build time.
Update the version constraint in composer.json:
"johnpbloch/wordpress": "^6.9"Then run composer update and redeploy.
The deploy script handles salts automatically:
- Option A: Define
AUTH_KEYthroughNONCE_SALTdirectly in.env— useful when sharing salts with local Docker Compose - Option B: Leave them empty —
deploy.shwill fetch salts from the WordPress API and cache them incloudrun/wp-salts.env
This project is configured for immutable operations:
DISALLOW_FILE_MODSis enabled inwp-config.php- Update checks for WordPress core/plugins/themes are disabled via MU plugin
Apply all WordPress/plugin/theme updates through composer.json + image rebuild/redeploy.
Key settings in cloudrun/service.yaml:
| Setting | Purpose |
|---|---|
cpu-throttling: "false" |
Keeps CPU allocated so Litestream can flush writes immediately |
container-dependencies |
Ensures Litestream restores the DB before WordPress starts |
execution-environment: gen2 |
Required for GCS FUSE support |
These files are in .gitignore:
.env— your project-specific configurationcloudrun/wp-salts.env— generated auth salts
Contributions are welcome! Please open an issue or submit a pull request.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is open source and available under the MIT License.