Lessons Learned: Using Git Workflows to Manage a Multilingual Festival Website

When I joined the core organizing team for Ring on Feier - Festival of Light, a volunteer-run festival in Zittau in the tri-national region of Germany, Czechia and Poland, I saw an opportunity. The single-day festival hadn’t taken place in six years, and for 2025, we didn’t know how many people would show up — we expected thousands, but there was high uncertainty. One thing was clear however: the festival website needed to be multilingual, reliable and operate on a shoe-string budget.
At the same time, I was continuing work on my own project: Commitspark, a set of open source Git-based data management tools designed to handle structured data with Git workflows. I decided to use this festival project as a more ambitious real-world proof of concept compared to the smaller sites I had previously built with it.
Here’s what worked, what didn’t, and why Git-based workflows completely changed the way I think about content management.
Why Git for Content?
In the past, I was technical lead for a very large multi-national roll-out of headless CMS Contentful and one thing that continuously frustrated me was how difficult it was to solve the following challenge:
How to keep the following aligned at all times in production?
- Content schema,
- Content,
- Frontend code.
In software development, we've already got a solution for such alignment issues by maintaining our code in Git throughout the entire code lifecycle:
Create → Iterate → Review → Merge / Publish → Maintain → Retire
My realization?
That’s exactly the lifecycle of content, too. We just aren't able to leverage Git — yet.
With Commitspark, I solved this challenge the following way:
Both the content schema and the content itself live together in Git. This means schema and content can always remain perfectly aligned. And with Git branches, we can easily keep schema changes away from production until frontend developers have updated their code to match.
Using Git for schema and content also unlocks a number of additional benefits:
- Branch-based workflows for staging and translations.
- Seamless, unified history for content and schema together.
- Easy recovery in case of issues (rollbacks, reverts, branches).
- Production-proven low-cost infrastructure via GitHub or other Git providers.
- CI/CD integrations for automated content validation and deployment.
For this festival website, Git workflows for content management weren't just theory. They saved me multiple times when mistakes slipped through during the quick-paced pre-festival rush.
Requirements & Challenges
The Ring on Feier team had three key requirements for the website:
- Multilingual content: German, Czech, Polish, and English for visitors from our region and further away.
- High reliability: The site had to remain fast and available during the festival, taking into account that crowd size was hard to predict.
- Low cost: This is a volunteer-run festival, so hosting and tooling budgets were minimal.
This meant that traditional headless CMSs like Contentful or Sanity were overkill — expensive and too complex for this scale, even though I had lots of experience with Contentful. On the other hand, static site builders like Decap CMS felt too limited for structured multilingual content and I did not want to learn the schema definition language of yet another tool.
As you'll see below in a moment, Commitspark was a perfect middle ground.
Implementation Overview
- Frontend: Next.js, self-hosted on a small DigitalOcean Kubernetes cluster.
- Media Hosting: Cloudinary.
- Content Storage: GitHub repository.
- Content Schema: GraphQL types defined in a plain text file.
- Content Data Format: YAML files, one-to-one with the schema.
- Content Access: Commitspark GraphQL API library.
By using standard GraphQL, I didn't have to define or learn yet another content schema language and anyone with knowledge of GraphQL is also immediately able understand how the content model is defined.
For example, here’s an excerpt of the schema file that I used to define the festival site content model:
# commitspark/schema/schema.graphql type LocalizedPage @Entry { id: ID! locale: Locale! slug: String! contentSections: [ContentSection!]! metaData: MetaData! } enum Locale { de cs pl en } type ContentSection { contentElements: [ContentElement!]! style: ContentSectionStyle } union ContentElement = | HeroElement | TextElement | CallToActionElement | ActivityListElement | SponsoringElement | ReusableComponentElement | TeamElement # ...
And the matching YAML content file for the main English language landing page:
# commitspark/entries/Home - EN.yaml metadata: type: LocalizedPage referencedBy: - Header - EN # ... data: locale: en slug: "" contentSections: - contentElements: - HeroElement: heading: Ring on Feier subHeading: Festival of Light backgroundImage: alt: Color gradient from yellow to red image: id: gradient-gelb-rot.jpg - SingleImageElement: singleImage: alt: Photo of the Building Trades School at night. The building is illuminated inside with different colored lights and illuminated outside with white dots. image: id: 2025-08-31 Baugewerkeschule.jpg imageSize: large imageDisplay: Cover # ...
Other implementation notes:
- Content queries with GraphQL: In Next.js, content was retrieved by issuing GraphQL queries against the Commitspark API library.
- No dedicated CMS hosting: GitHub acted as the entire content backend, with the Commitspark API library fetching YAML files from GitHub and then resolving queries in-memory.
- Dynamic content editing UI: In most cases, I modified content using the Commitspark content editing UI that builds itself on the fly from the schema in a branch, and commits my content changes directly back to the Git repository — much easier than working with raw YAML files.
- Offloaded media to Cloudinary: I extended the editing UI with a pre-commit hook to upload media files to Cloudinary instead of committing these into the repository, keeping only image filename, hash and metadata for storage in Git.
Git Workflows for Content in Practice
-
Branches for non-trivial changes: Dedicated branches for preparing bigger changes like a landing page rework or when adding a new translation early in the project allowed me to independently work on different content at different speeds.
-
Pull requests (PRs): I reviewed every PR as I would have done in software development, catching mistakes like:
- A donation button linked to the wrong language.
- A sponsor element accidentally used a news photo instead of sponsor logo.
- A page I had saved under the wrong locale.
-
Schema changes: Made in separate branches which enabled me to add and modify content elements without stress, as I was not holding up time-critical changes to production content in the meantime.
-
Previews: By pointing frontend GraphQL queries to different content branches, I could selectively preview and validate changes per branch, without having to worry about unrelated modifications slipping in.
This Git workflow meant no broken content ever reached production — even during the busiest days leading up to the festival.
Performance and Reliability
GitHub APIs are subject to strict rate limits, so I used Next.js caching to keep GitHub requests low:
- Next.js
unstable_cache()
with a 20-minute revalidation timeout was quick and easy to implement and more than sufficient for the speed of content iteration we needed for the festival site. - Using GitHub actions to explicitly trigger Next.js cache invalidation on commits to the content repository would also have been possible, but I didn't bother setting this up.
Results:
- Google PageSpeed Insights performance score of 99 and 100 (mobile / desktop).
- LCP: 1.2s | TTFB: 0.5s.
- No downtime at any point.
- Load test: 1,000 concurrent users → response time still 4s.
- GitHub rate limits? Never hit.
Quantitative Wins
- Hosting costs: $0 for GitHub and Cloudinary, plus $68 per month for a DigitalOcean Kubernetes cluster that I also use for other projects.
- Time to build: 4 weeks of part-time work, with only around one hour for the content schema.
- Content published: 30+ pages across 4 languages, plus 100+ images.
- Content updates: 25 Pull Requests (PRs) and around 3000 commits (before squashing).
- Traffic handled: 3,800 unique site visitors on festival day.
- Festival visitors: Between 10,000 to 15,000 with very positive feedback.
Lessons Learned
What worked well:
- The Git-based workflow using branches for handling multilanguage content took away as much stress as I had hoped.
- Content modeling with GraphQL felt really intuitive, especially for this non-trivial site.
- The entire setup was incredibly lean and stable to run at very low cost.
What needs improvement:
- The Commitspark editing UI lacks many convenience features which made some editing tasks (e.g. reordering content) so tedious that I simply modified the YAML content files by hand instead.
- The YAML content folder became a bit cluttered with 100+ entries. I need to add support for subfolders.
- Bigger teams and those with team members with little knowledge about Git will find there are not enough collaboration features included directly in the editing UI at this time.
Who Should Try This
Using Commitspark to power a multilanguage website with GitHub is something for you if:
- You want the same control over content and schema that developers have over code.
- You’re a freelance dev or small agency with a tech savvy team wanting to keep the tech stack light and based on proven standards when building multilingual sites.
- You like the structured approach of Sanity or Contentful, but you're on a limited budget and without need for enterprise features.
Commitspark is open source and available on GitHub under a permissive license. You can get started by creating a copy of the multilanguage sample repo.
If you're interested in a copy of the full festival site content schema, reach out to me via email, Mastodon or LinkedIn.
Final Thoughts
Building the Ring on Feier site reinforced my belief that content and code follow the same workflow principles and we should therefore handle them with the same tools.
Doing any different would be the same as trying to manage software code without Git: Possible, but risky and unproductive. In other words: A bad idea.
You can see the live festival site here: ring-on-feier.de
And check out Commitspark on GitHub to try it yourself.
If you found this interesting, feel free to reach out to me directly. I’d love to hear what you think about using Git workflows for managing content.