As a software engineer with a thirst for adventure, I'm constantly looking for ways to
apply engineering rigour to problems that most people solve manually. A resume is one of
those problems. It goes stale, the formatting drifts, version history disappears into a
graveyard of final_v3.docx files, and every update turns into a tedious afternoon of
pixel-pushing. I decided to treat my resume the same way I treat production software:
as source code, with automation, versioning, and a proper release pipeline. This
repository is the result.
The instincts that make engineers effective — reproducibility, automation, auditability — apply just as well to a resume as they do to any other deliverable. Here is why I think every engineer should have one of these:
Your resume is source code. The single source of truth lives in resume.json, a
structured file that follows the open JSON Resume schema. Every
change is a commit. The full history is preserved. There are no conflicting copies,
no formatting accidents, and no mystery about what changed between versions.
Every push ships a release. The moment you push, the CI pipeline takes over. It builds a Docker image, renders the HTML, generates the PDF, minifies the page, deploys it to GitHub Pages, force-amends the commit to include the built artifacts, and tags a new versioned release — all without you touching a single tool manually. The pipeline does in seconds what used to take an afternoon.
One Docker command, anywhere. The build is fully containerised and produces identical output on Intel, Apple Silicon, and ARM servers. There is no "it works on my machine" problem. Anyone can clone the repo, run a single command, and have a pixel-perfect PDF in their working directory.
The pipeline is intentionally simple:
| Step | What happens |
|---|---|
Edit resume.json |
Update your content — work experience, skills, education |
git push |
Triggers the GitHub Actions workflow |
| CI builds the image | Multi-arch Docker image pushed to Docker Hub |
| Artifacts are generated | resume.pdf, resume.html, and two preview PNGs |
| HTML is minified | CSS, JS, and whitespace compressed for the web |
| GitHub Pages is updated | The minified HTML is live at your Pages URL |
| Release is tagged | A versioned GitHub release is created with resume.pdf as an asset |
Under the hood, resumed validates the JSON and renders it through a custom Handlebars template styled with TailwindCSS v4. Puppeteer then takes the HTML and prints it to PDF using a headless Chromium instance baked into the Docker image. No external services, no proprietary tooling — just open-source software running in a reproducible container.
At the very top of this page you can click João's Resume | VIEW to open the PDF directly
in your browser, or João's Resume | DOWNLOAD to save a copy for offline viewing.
For the tech-savvy, the raw data is in
resume.json— the file that drives everything. Here is a preview of what it looks like:
The full workflow is defined in
.github/workflows/resume.yml. You can watch it run live on GitHub Actions.
The only dependency is Docker. Once installed, you have two options:
Option 1 — Pull the pre-built image from Docker Hub (fastest):
docker pull jpcercal/resumeOption 2 — Build the image locally (after making changes to the Dockerfile):
docker build --tag jpcercal/resume .Either way, generate all artifacts with a single command:
docker run --rm -v "$PWD:/app" jpcercal/resumeWindows PowerShell: replace
"$PWD"with"${PWD}".
This mounts the project root as /app inside the container and writes four files directly
into your working directory:
resume.pdfresume.htmlresume.pdf.pngresume.json.png
The image is built for both linux/amd64 and linux/arm64, so it runs natively on
Intel/AMD machines as well as Apple Silicon (M-series) and ARM servers — no emulation
overhead.
Enjoy it. =)
The best part about treating a resume as a repository is that the whole pipeline is
forkable. Clone or fork this repo, edit resume.json with your own information, push,
and your personal resume pipeline is live — complete with GitHub Pages hosting, versioned
PDF releases, and a Docker image on Docker Hub.
If the default layout does not suit your style, the entire theme lives in the theme/
folder: swap the Handlebars template, adjust the TailwindCSS tokens, add new helpers.
The resume.json data model stays the same; only the presentation changes.
Your resume is a living document. It should be treated like one.