The Dark Side of Free Website: Limits and Alternatives for Hugo + GitHub + Cloudflare Pages + Pages CMS

In this post
In a previous post, we put together a complete blog with Hugo, GitHub, Cloudflare Pages, and Pages CMS without spending a cent. The stack works, it is fast, and for most personal blogs it will keep working for a long time without asking anything in return. But “free” does not mean “without limits,” and understanding where the walls are before you hit them is the kind of thing that saves headaches down the road.
This post is the practical companion to that tutorial. Here we will look at the real limits of each free service in the stack, discuss in which scenarios they start to squeeze, and list alternatives for when — or if — that happens. No installation walkthroughs; the idea is to give you the general map so you can make informed decisions.
The Price of “Free”#
Free services for developers exist for very concrete reasons. GitHub wants you and your team to use the platform until it makes sense to pay for the Team or Enterprise plan. Cloudflare wants your domain going through their network, because more sites mean more data about attacks and more sales arguments for corporate clients. Pages CMS is an open source project maintained by a single developer who needed the tool and decided to share it.
None of these motivations are bad — in fact, the alignment of incentives is what makes the model work. GitHub is not going to pull your free repository tomorrow, and Cloudflare is not going to charge for the bandwidth of your recipe blog. The point is that each of these services has limits designed to separate personal and small project use from commercial use at scale, and knowing those limits is part of using the stack with confidence instead of hope.
In the following sections, we will go through each piece of the stack — GitHub, Cloudflare Pages, and Pages CMS — with concrete numbers and enough context for you to assess whether any of them are relevant to your case.
GitHub Free: the Repository Has Walls#
GitHub’s free plan is extraordinarily generous for what most people need: unlimited public and private repositories, unlimited collaborators on public repos, and enough features to run serious projects without paying anything. But generous is not infinite, and the limits that exist are in places that directly affect anyone maintaining a static site.
Repository and File Size#
GitHub recommends that repositories stay under 1 GB and strongly insists they do not exceed 5 GB. There is no published hard limit for total size — what happens in practice is that above 5 GB, support reaches out asking you to reduce the size or change your approach. Individual files have a real limit of 100 MB via command line; through the browser, the ceiling drops to 25 MB. Any push with a file over 100 MB is simply rejected.
For a Hugo blog, this is rarely a problem. Markdown weighs almost nothing, and the entire repository of a blog with hundreds of posts barely gets past a few dozen megabytes. The risk lives in images. If you version high-resolution photos directly in the repository instead of optimizing them before committing, the Git history accumulates every version of every file, and the repo size grows in a way that is not obvious until you try to clone it and realize you are downloading gigabytes of old JPEGs.
Git LFS: 1 GB of Storage, 1 GB of Bandwidth#
For large files that genuinely need to be in the repository, GitHub offers Git Large File Storage. LFS stores the file outside the repo and leaves a lightweight pointer in its place. The free plan includes 1 GB of storage and 1 GB of bandwidth per month — and both limits are tighter than they seem. Each push of a file consumes storage cumulatively (pushing the same 500 MB file twice exhausts the quota), and each download by any person or CI consumes bandwidth. For a personal blog with optimized images, you will probably never need LFS. But it is good to know the limit exists and that if you hit it, pushes are silently rejected.
GitHub Actions: 2,000 Minutes for Private Repos#
GitHub Actions is free and unlimited for public repositories using standard runners. For private repositories, the Free plan includes 2,000 minutes per month of execution on Linux runners — equivalent to over 33 hours of CI/CD. If your Hugo repository is public, this limit simply does not apply. If it is private, the 2,000 minutes are more than enough for most workflows: a typical Hugo build takes less than a minute, so you would need to make more than 60 deploys per day to get close to the ceiling.
Worth noting that Cloudflare Pages already handles build and deploy when you connect the repository, so most Hugo blogs on this stack do not even use GitHub Actions for deployment. The minutes only come into play if you set up additional workflows — linting, tests, image optimization, that kind of thing.
The Public vs. Private Repo Detail#
The choice between a public and private repository changes the limits equation considerably. With a public repo, you get unlimited Actions and full code visibility — which for a blog is not necessarily a problem, since the content will be published anyway. With a private repo, you gain privacy over drafts and configurations but enter the Actions and Packages quota regime. For most personal blogs, keeping the repository public is the simplest decision and the one that leaves the most room within the free plan. If you have reasons to keep the repo private — sensitive content in drafts, configurations you do not want to expose — the Free plan limits are still comfortable for a single site with moderate deploys.
Cloudflare Pages Free: Generous, but with a Ceiling#
Cloudflare Pages is by far the most generous piece of this stack. Unlimited bandwidth, unlimited requests for static content, automatic SSL, global CDN, unlimited sites — all on the free plan, no credit card required. It is the kind of offer that makes you suspicious, but the logic behind it is solid: Cloudflare wants your domain going through their network, and hosting static sites is computationally cheap enough to serve as a gateway. The limits that exist are not in traffic but in the build process and file scale.
500 Builds per Month — and It Is per Account, Not per Project#
Each push to the connected repository triggers a build on Cloudflare. The free plan allows 500 builds per month, and this is the limit that confuses people the most: it is per account, not per project. If you have three sites running on the same Cloudflare account, all three share the same 500-build quota. For a personal blog with one deploy per day, that is about 30 builds per month — comfortable even with multiple sites. The scenario where this gets tight is teams doing rapid iterations with multiple pushes per day across several projects, or anyone using preview deployments extensively during development.
In practice, the build limit is more a matter of workflow discipline than a technical restriction. Consolidating changes into fewer commits instead of pushing every comma solves the problem for almost everyone. And if 500 builds per month are not enough, the Pro plan increases it to 5,000 — but at that point you probably already have a commercial reason to pay.
One Build at a Time#
The free plan processes a single build at a time. If you push to two projects simultaneously, the second waits in line until the first finishes. For a Hugo blog, where the entire build takes seconds, this is imperceptible. The limitation becomes relevant for larger sites with heavier build processes — JavaScript frameworks with bundling steps, image optimization in the pipeline, that kind of thing — or for accounts with many projects deploying simultaneously. The Pro plan goes up to 5 concurrent builds, and Business to 20.
20,000 Files per Site#
Each site on the free plan can have a maximum of 20,000 files. This counts everything that is deployed: generated HTMLs, images, CSS, JavaScript, fonts, favicons. A medium-sized Hugo blog stays far from this number — even with hundreds of posts and images, you will hardly exceed a few thousand files. The limit starts to matter for large documentation sites with thousands of pages, or for projects that generate many assets per page. Paid plans raise the ceiling to 100,000 files.
Functions: the 100,000 Requests/Day Limit Comes from Workers#
If you use Cloudflare Pages Functions — serverless code running at the edge — requests count against the Workers Free plan quota, which is 100,000 requests per day, resetting at midnight UTC. This limit is shared between Pages Functions and any Workers you have on the same account. For a pure static blog, this does not apply — HTML, CSS, images, and static JavaScript do not consume this quota. It only comes into play if you add dynamic features to the site, like a contact form processed at the edge or a custom API. Even so, 100,000 requests per day is a considerable volume for a blog’s auxiliary features.
Pages CMS: Free, Open Source — and the Risks That Come with It#
Pages CMS occupies a different position from the other pieces of the stack. GitHub and Cloudflare are billion-dollar companies offering free plans as a commercial strategy; Pages CMS is an open source project created and maintained by Ronan Berder, a developer who needed a simple CMS for static sites and decided to share the solution. It is 100% free, MIT-licensed, and can be used either on the hosted version at app.pagescms.org or self-deployed on Vercel or self-hosted. There is no paid plan, no premium tier, no company behind it covering infrastructure costs with revenue from other products.
This is simultaneously the greatest quality and the greatest risk of Pages CMS.
Single-Developer Project#
Pages CMS does not have a team, no known public funding, and no organization sustaining its development. This is not a criticism — many of the best open source tools started exactly this way. But it means that the speed of bug fixes, the pace of new features, and the project’s very continuity depend on the availability and interest of a single person. If you follow the repository on GitHub, you will see periods of intense activity followed by quieter periods, which is perfectly normal for a project maintained in someone’s spare time.
For a personal blog, this is an acceptable risk. For a site that a business depends on to publish content regularly, it is the kind of fragility that deserves at least a Plan B.
Total Dependence on the GitHub API (and Its Rate Limits)#
Pages CMS has no database of its own for content. It reads and writes directly to the files in your GitHub repository using the GitHub API. This is elegant — your content never leaves the repository, there is no synchronization to break, and any change made through the CMS is a normal commit that appears in the Git history. But it also means that every operation in the CMS is subject to GitHub API rate limits: 5,000 requests per hour for authenticated users. In practice, editing posts and uploading images on a personal blog does not come close to this limit. The problematic scenario is a team with multiple editors working simultaneously on a repository with many files, where the CMS’s navigation and caching can generate a volume of API calls that starts to encounter throttling.
No Commercial Support or SLA#
If something breaks in Pages CMS — and software breaks — there is no support channel to open a ticket and expect a response within a defined timeframe. What exists is the project’s GitHub issue queue, where you can report the problem and hope the community or the maintainer responds. For those accustomed to the open source ecosystem, this is standard and works reasonably well. For those coming from the SaaS world with SLAs and chat support, the expectation gap can be frustrating. There is no uptime guarantee for the hosted instance at app.pagescms.org, and there is nobody on call if it goes down on a Sunday night.
What Happens If the Project Is Abandoned#
This is the question nobody likes to ask about open source projects they use, but that every responsible adult should consider. If the Pages CMS maintainer decides to stop maintaining the project tomorrow, your content stays exactly where it has always been: in your GitHub repository, in Markdown files with YAML front matter. You lose nothing. What you lose is the editing interface — and then you go back to editing content directly on GitHub or in a local text editor, which is not the end of the world but is exactly the inconvenience the CMS existed to solve. The MIT license guarantees that anyone can fork and continue development, but between a project’s abandonment and the emergence of a viable fork there is usually a limbo period that can last months.
The fact that the content is portable — pure Markdown in a Git repository — is the best protection this architecture offers. You are never locked into a specific CMS, and migrating to any alternative is a matter of reconfiguring the editing tool, not exporting data from a proprietary database.
When These Limits Actually Matter#
Listing limits out of context makes them seem scarier than they should be. Five hundred builds, twenty thousand files, two thousand minutes — these are numbers that look restrictive until you put a real use case next to them and realize that most people will never come close. What matters is not the absolute number but the relationship between your usage and the available ceiling.
The Solo Blogger Will Probably Never Hit Them#
A personal blog with one author publishing a few posts per month is the scenario this free stack was practically designed for. Do the math: if you publish three times a week and do one push per post, that is about 12 builds per month — 2.4% of the Cloudflare quota. The repository of a blog with five hundred Markdown posts, optimized images, and the entire Hugo theme will barely pass 200 MB. The GitHub API calls from Pages CMS to edit a post and upload an image fit comfortably within a tiny fraction of the 5,000 requests per hour rate limit. In this scenario, the limits are so distant they function as if they did not exist.
Multiple Sites on the Same Account Change the Equation#
The math changes when you start stacking projects. The Cloudflare Pages 500-build quota is per account, so five sites with 100 builds each already exhaust the month. The same goes for GitHub Actions minutes on private repos — the 2,000 minutes are shared across all repositories on the account. If you are the kind of person who likes to maintain a personal blog, a portfolio site, a landing page for a side project, and documentation for a tool you wrote, each project alone is lightweight, but the sum can start to push against the limits.
The simplest solution is to keep projects in separate accounts when it makes sense, or accept that consolidation has a cost in terms of quota. Another approach is to reduce unnecessary builds: disable automatic deployment of branches that do not need previews, batch changes into fewer commits, and configure ignore patterns in Cloudflare Pages so that pushes that do not alter the site’s content do not trigger builds.
Teams with Multiple Editors and Frequent Deploys#
The scenario where limits actually start to bite is a team — even a small one — with multiple people editing content and deploying throughout the day. Each save in Pages CMS is a commit, each commit on a connected branch is a build. Three editors actively publishing and reviewing content can generate dozens of builds per day without realizing it. The free plan’s single concurrent build means those builds queue up, and the monthly quota of 500 starts to feel less comfortable.
On the Pages CMS side, multiple editors browsing and editing simultaneously multiply the GitHub API calls. The 5,000 requests per hour rate limit is per authentication token, so in practice each editor has their own quota — but if the team shares a single installation with one GitHub App, the limit is shared. Additionally, repositories with many files make the CMS work harder to load and cache the directory structure, which can result in a slower experience and cache bugs that have already been reported in the project’s issue queue.
For teams fitting this profile, the free stack still works, but it demands more attention to workflow. And it is exactly the point where it makes sense to evaluate whether a paid plan on one end — Cloudflare Pro for more builds, or a CMS with commercial support — does not pay for itself in saved time and frustration.
Alternatives at a High Level#
If you have read this far and concluded that some limit of the free stack is relevant to your case, the good news is that each piece can be replaced independently. The static site architecture with a Git repository is modular by nature — you can swap hosting without swapping the CMS, swap the repository without swapping the generator, and so on. What follows is not a detailed analysis of each alternative but a map of options so you know where to look.
For the Repository: GitLab, Codeberg, Self-Hosted Gitea#
If GitHub Free’s limits are the problem — repository size, CI minutes, or simply a preference for not depending on a specific platform — mature alternatives exist. GitLab offers unlimited private repositories with 400 CI/CD minutes per month on the free plan and has its own Pages system for static hosting. Codeberg is a nonprofit alternative based on Forgejo, with no artificial CI limits and an explicitly free software philosophy. For those who want full control, Gitea or Forgejo can be installed on any server — a cheap VPS is more than enough to host Git repositories with integrated CI.
The main consideration when switching Git platforms is the cascading effect on the rest of the stack. Pages CMS depends specifically on the GitHub API, so moving the repository to GitLab means also switching the CMS. Cloudflare Pages integrates natively with both GitHub and GitLab, so that part of the migration is straightforward.
For Hosting/Deploy: Netlify, Vercel, GitHub Pages, Caddy/Nginx + Your Own CI#
Cloudflare Pages is not the only option for hosting static sites with automatic deploy from a Git repository. Netlify offers a free plan with 300 build minutes per month and 100 GB of bandwidth — less generous than Cloudflare on bandwidth, but with a plugin ecosystem and features like forms and identity that can simplify certain use cases. Vercel has a free plan aimed at JavaScript frameworks but works perfectly with Hugo and offers 100 GB of bandwidth per month. GitHub Pages itself is a solid option for Jekyll and Hugo sites, with generous bandwidth and deploy straight from the repository — the main limitation is the lack of custom builds without GitHub Actions and more limited support for advanced routing configurations.
For those who want to leave managed platforms entirely, the combination of a server with Caddy or Nginx, a lightweight CI like Drone or Woodpecker, and deploy via rsync or webhook offers absolute control. The cost is a VPS at five or ten dollars a month and the time for setup and maintenance — which for a sysadmin is trivial, but for someone who set up their blog following a tutorial can be a considerable leap in complexity.
For the CMS: Decap CMS, Sveltia CMS, TinaCMS, Publii#
Pages CMS is one of several CMS options for Git-based static sites, and if the risks of depending on a single-maintainer project do not sit well with you, alternatives exist with different profiles of maturity and support.
Decap CMS, formerly Netlify CMS, is the veteran of the space. It is open source, works as a single-page app running on the site itself at an /admin route, and commits directly to the repository. It has a large community and is the most battle-tested in production, but development has slowed in recent years and the editing experience is not the most modern. Sveltia CMS aims to be a drop-in replacement for Decap with a faster and more polished interface while maintaining compatibility with the same configuration — worth considering as a direct alternative if Decap works for your workflow but the interface bothers you.
TinaCMS is the most ambitious option in the group. It offers real-time visual editing on the site itself, supports Markdown and MDX, and has both a self-hosted version and a cloud service with a free plan. The trade-off is setup complexity, which is significantly higher than Pages CMS, and a stronger dependency on the JavaScript ecosystem and frameworks like Next.js — although it works with Hugo, the integration is not as seamless.
Publii follows an entirely different philosophy: it is a desktop application for Windows, Mac, and Linux that works as a local CMS. You edit content on your machine, Publii generates the static site, and deploys to GitHub Pages, Netlify, S3, or any server via FTP. It does not depend on any API, does not need internet to edit, and eliminates the entire web CMS layer. The downside is that content lives on the machine where Publii is installed, and the collaborative workflow with multiple editors is not its strong suit — in fact it is practically impossible to maintain a blog with multiple authors on Publii.
In every case, the fact that the content is Markdown with front matter in a Git repository means that migrating between these tools is a matter of reconfiguration, not data conversion. You chose an architecture where content is independent of the editing tool, and that is the most valuable decision in the entire stack.
My Take: Start with Everything Free#
After listing limits, risks, and alternatives, it would be easy to close this post with a cautious recommendation — something like “evaluate carefully before choosing” or “every case is different.” But the truth is simpler than that: for anyone starting a blog or personal site, the free stack with Hugo, GitHub, Cloudflare Pages, and Pages CMS is the best choice available today, and it is not a compromise.
The limits exist, but they are designed for a usage volume that the vast majority of personal sites will never reach. Cloudflare’s unlimited bandwidth eliminates the most common worry for anyone hosting their own content. The 500 monthly builds are more than enough for any reasonable publishing pace. GitHub Free offers everything a blog repository needs without charging a thing. And Pages CMS, despite the inherent risks of a single-maintainer project, solves the real problem of editing content without needing to open a terminal.
Most importantly, this stack does not lock you in. Your content is Markdown in a Git repository — the most portable format that exists for text on the web. If tomorrow Pages CMS is abandoned, you switch CMSes. If Cloudflare changes the free plan terms, you move the site to Netlify in an afternoon. If GitHub does something you disagree with, your files are on your local disk and can go to any other Git service with a git remote set-url. Every decision in this architecture is reversible, and that is worth more than any SLA guarantee.
Start with everything free. Publish. Write. When — and if — any limit becomes real in your actual usage, you will know exactly which piece to swap and why, because the problem will be specific and not hypothetical. Optimizing before you have a problem is premature engineering, and premature engineering is the most efficient way to never publish anything.
If one of the gaps you notice first is the lack of native comments, I already wrote about how to solve that with a lightweight, self-hosted solution with no tracking.
Sysadmin, 53, Brazilian working from home for the world. Manages Linux servers, LXC containers, and cats that won't get off the keyboard.