How to Deploy a Hugo Site to Cloudflare Pages With Github Actions
We recently moved this website from Django to a static website made using Hugo, and
decided to use Cloudflare Pages to deploy it.
Cloudflare Pages offers automatic deployments, meaning it can automatically build
and deploy a website when changes are pushed to a GitHub branch. This is simple
to set up and works well. However, we wanted to build and deploy using GitHub
Actions instead, so that the build logs would be easily accessible on GitHub.
Cloudflare’s wrangler-action
makes it simple to set up a GitHub Actions workflow for this.
Create a Cloudflare Pages project, with automatic deployments disabled.
Create an API token and add it, along with your account ID, as secrets in your GitHub settings. See Cloudflare’s docs for details.
Create a YAML file in the
.github/workflows
directory in your Git repository with the content below. There are some comments with more details. Remember to replace “your-project-name” with the name of your Cloudflare project.on: # This will deploy the production site when changes are pushed to the "main" branch. # The branch name should be the one you set up as the "Production branch" in your Cloudflare Pages project. push: branches: [main] # This will deploy preview sites when a PR is opened/reopened and when changes are pushed to the PR. pull_request: # Allow manual deploys via the GitHub Actions Web UI. workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest name: Deploy steps: - uses: actions/checkout@v4 with: # Fetch all history for Hugo's .GitInfo and .Lastmod fetch-depth: 0 - name: Cache Hugo resources uses: actions/cache@v3 with: path: resources/_gen key: ${{ runner.os }}-hugo-resources-${{ github.ref_name }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-hugo-resources-${{ github.ref_name }}- ${{ runner.os }}-hugo-resources-main- - name: Cache node_modules uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ github.ref_name }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-${{ github.ref_name }}- ${{ runner.os }}-node-main- - name: Setup Hugo uses: peaceiris/actions-hugo@v3 with: # Update the Hugo version if necessary. hugo-version: "0.148.2" extended: true - name: Build run: hugo --minify --logLevel=debug - name: Deploy id: deploy uses: cloudflare/wrangler-action@v3 # Update 'your-project-name' below with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} # Deploy using `page deploy` wrangler command # See: https://developers.cloudflare.com/workers/wrangler/commands/#deploy-1 # `github.head_ref` will only be available during a `pull_request` event, and will # contain the name of the current branch. `github.ref_name` will be available for all # events and will also contain the current branch's name, but during a `pull_request` # its format is `<pr_number>/merge` which is not what we want. # See: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#github-context command: pages deploy public --project-name=your-project-name --commit-dirty --branch ${{ github.head_ref || github.ref_name }} - name: Print preview URL run: echo "${{ steps.deploy.outputs.deployment-url }}" - name: Print branch preview URL run: echo "${{ steps.deploy.outputs.pages-deployment-alias-url }}" - name: Get short SHA of latest commit in PR id: short-sha if: github.event_name == 'pull_request' run: | HEAD_SHA="${{ github.event.pull_request.head.sha }}" echo "short_sha=${HEAD_SHA:0:7}" >> $GITHUB_OUTPUT - name: Comment on PR uses: thollander/actions-comment-pull-request@v3 if: github.event_name == 'pull_request' # Update 'your-project-name' below with: message: | ## Deployed your-project-name with Cloudflare Pages | **Latest commit:** | `${{ steps.short-sha.outputs.short_sha }}` | |:-|:-| | **Status:** | ✅ Deploy successful! | | **Preview URL:** | ${{ steps.deploy.outputs.deployment-url }} | | **Branch Preview URL:** | ${{ steps.deploy.outputs.pages-deployment-alias-url }} | comment-tag: cf-preview-url
That’s it! Now any time a PR is opened, a preview site will be deployed, and a message
will be posted in the PR showing the URLs you can use to access the preview site:
When changes are pushed to the PR, the preview site will be redeployed, and the same message will be updated.
And when you merge the PR into your production branch, the production site will be deployed.