๐Ÿ˜ Jumbo
๐Ÿ“ On this page

Deployment

A built site is plain files, so any static host can serve it: Netlify, Cloudflare Pages, GitHub Pages, S3, bunny.net or a folder on a web server.

vendor/bin/jumbo build --url=https://acme.dev

Upload dist/. Set url (in config or with --url) so canonical links, the sitemap and feeds have absolute URLs.

GitHub Pages

GitHub Pages serves project sites from a sub-path, like https://you.github.io/acme. Put the path in the URL and Jumbo adds it to every link:

# .github/workflows/pages.yml
name: Pages

on:
  push:
    branches: ["main"]

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
      - id: pages
        uses: actions/configure-pages@v5
      - run: composer install --no-progress
      - run: vendor/bin/jumbo build --url=${{ steps.pages.outputs.base_url }}
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
      - id: deployment
        uses: actions/deploy-pages@v4

Set the repo's Pages source to GitHub Actions in its settings. These docs deploy the same way.

A PHP server

Pages with actions, or with prerender: false, need PHP to run. Add a front controller next to app.php:

<?php // index.php

(require __DIR__ . "/app.php")->run();

Then send every request to index.php with DUMBO_ENV=production set. Jumbo serves public/ itself, so the web server doesn't need to serve any files. With Nginx and PHP-FPM:

location / {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    fastcgi_param DUMBO_ENV production;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Turn on OPcache, and give images.cache a folder that survives deploys so resized images aren't made twice. Dumbo's deployment guide covers Apache, FrankenPHP and Docker, and all of it applies to a Jumbo site.

You can also mix the two: build the static pages for a CDN, and run the server for the pages that need it.

bunny.net

bunny.net can host a built site on its CDN, or run the server in a container with Magic Containers. Both use the bunny CLI:

npm install -g @bunny.net/cli
bunny login

Static sites

Build the site and deploy dist/:

vendor/bin/jumbo build --url=https://acme.dev
bunny sites deploy dist

The first deploy creates the site, a storage zone with a pull zone in front, and links the folder to it. Every deploy after that goes live straight away, and bunny sites deployments publish --previous rolls back to the one before. Jumbo writes 404.html, which the site serves for missing pages.

To build and deploy with one command, put both in a bunny.jsonc next to app.php:

{
  "sites": {
    "name": "acme",
    "dir": "./dist",
    "build": "vendor/bin/jumbo build --url=https://acme.dev"
  }
}
bunny sites deploy --build
bunny sites domains add acme.dev

To deploy on every push, add an API key from your bunny.net account as the BUNNYNET_API_KEY secret in the repo, and a workflow:

# .github/workflows/bunny.yml
name: Deploy

on:
  push:
    branches: ["main"]
  workflow_dispatch:

concurrency:
  group: bunny-sites
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      deployments: write
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"
      - run: composer install --no-progress
      - run: vendor/bin/jumbo build --url=https://acme.dev
      - uses: BunnyWay/actions/deploy-site@deploy-site_0.1.0
        with:
          site: acme
          directory: dist
          api_key: ${{ secrets.BUNNYNET_API_KEY }}

Turn on Bunny Optimizer for the site's pull zone and set the images loader to new Bunny() to have the CDN resize images instead of the build. See Images.

Magic Containers

Sites with actions, or pages with prerender: false, can run in a container on Magic Containers, with bunny.net's CDN in front. Add the index.php front controller above, then a Caddyfile for FrankenPHP that sends every request to it and keeps the site in memory with worker mode:

{
	frankenphp {
		worker /app/index.php
	}
}

:8080 {
	root * /app
	rewrite * /index.php
	php
}

And a Dockerfile:

FROM dunglas/frankenphp:php8.3-alpine

RUN install-php-extensions gd exif
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# Swap the image's welcome page for the site
WORKDIR /app
RUN rm -rf /app/*
COPY . .
RUN composer install --no-dev --optimize-autoloader

COPY Caddyfile /etc/frankenphp/Caddyfile
ENV DUMBO_ENV=production

Leave vendor/, dist/ and node_modules/ out of the image with a .dockerignore, then deploy:

bunny apps deploy --dockerfile --port 8080

The first deploy asks for a name, region and container registry, and writes a bunny.jsonc to commit. After that, bunny apps deploy --dockerfile builds and ships each new version.

Containers start with an empty disk, so resized images are made again after each deploy. The CDN keeps the copies it has already served, or turn on Bunny Optimizer for the app's pull zone and use the Bunny images loader so the container never resizes them at all.