The pipeline tests your code and publishes a fresh image to GHCR on every merge. One manual step survives from last week: telling Render about it. Today the pipeline takes over that click too — and the loop from git push to live URL closes completely.
Last week Render built your app from the repository. Now that your pipeline already produces an image, Render should simply pull and run it — this is called an image-backed service. Create one (or reconfigure): New → Web Service → Existing image, with the image URL:
ghcr.io/<your-username>/<your-repo>:latest
This works because you made the package public in the previous chapter. Keep your environment variables exactly as they were — DATABASE_URL and friends for Neon, plus SPRING_PROFILES_ACTIVE=prod so the behaviour from the Spring profiles chapter kicks in. The image is identical everywhere; the environment supplies values, the profile sets behaviour.
<aside> 💡
Notice the division of labour now: GitHub stores code, GHCR stores images, Render runs them, Neon keeps the data. Each service does the one thing it is for — and the pipeline is the conveyor belt between them.
</aside>
One catch, straight from Render's documentation: an image-backed service does not notice when a new image appears in the registry. Someone must tell it to redeploy. That someone is your pipeline.
Every Render service has a deploy hook: a unique URL that triggers a deploy when anything sends a request to it. Find it under your service's Settings → Deploy Hook.
That URL is a secret — anyone who has it can deploy your service. It must not appear in your workflow file, because the workflow file is committed to Git. GitHub has a home for exactly this: in your repository, Settings → Secrets and variables → Actions → New repository secret. Name it RENDER_DEPLOY_HOOK and paste the URL.
<aside> 💡
You now know both kinds of pipeline secret. GITHUB_TOKEN is minted by GitHub per run — you never see it. RENDER_DEPLOY_HOOK is yours to manage — you store it once, the workflow reads it as ${{ secrets.RENDER_DEPLOY_HOOK }}, and GitHub masks it in every log. All the secrets you will meet at work are one of these two kinds.
</aside>
The final job is the shortest — one request:
deploy:
runs-on: ubuntu-latest
needs: build-and-push
if: github.ref == 'refs/heads/main'
steps:
- name: Trigger Render deploy
run: curl --fail --silent --show-error "${{ secrets.RENDER_DEPLOY_HOOK }}"
needs: build-and-push keeps the chain honest: deploy only runs when a freshly tested image has actually been pushed. --fail makes curl return an error on a bad response — so a mistyped secret turns the step red instead of silently “succeeding”.
Your whole pipeline now reads as one sentence: on every merge to main — test, package, ship.
PR opened → test
merge to main → test → build-and-push → deploy → 🌍 live
<aside> ⌨️
Hands on: prove it end to end. Change something visible — the response of an endpoint, a version string — on a branch. Open a PR, watch the tests, merge, and then just watch: Actions goes green, Render's Events page shows a deploy starting, and a minute later your live URL serves the change. You touched nothing but Git.
</aside>
<aside> 🎉
That was continuous deployment — the thing from the first chapter's table, running on your own project. From now on, git merge is how your software ships.
</aside>
One day a bug will reach production anyway — tests cannot catch everything. This is why every build got a commit SHA tag. To roll back, tell Render to run a previous image: in the service's settings, change the image tag from latest to the last good SHA and deploy — or append it to the hook call, as Render's deploy-hook documentation shows:
curl "$RENDER_DEPLOY_HOOK&imgURL=ghcr.io/<your-username>/<your-repo>:<previous-sha>"
No rebuilding, no reverting commits under pressure — the old image still exists, running it again takes one minute. This is why latest alone was never enough.
At work you will usually see one more environment in the chain: the pipeline deploys to a staging service automatically, and promotion to production waits for a human click — GitHub calls this an environment with required reviewers. Same pipeline, same images; the only difference is who presses the last button. You already know that distinction by name: it is Continuous Delivery.
💬 Question: the pipeline is green end to end, but the live site still shows the old version. Name two different places the problem could hide.
imgURL parameter for deploying a specific tag, and their own GitHub Actions example.The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with ❤️ by the HackYourFuture community · Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.