I love Netlify. I think it’s a wonderful service that provides tremendous value to many developers and teams. But, like so many things in the modern world, I think it’s easy to overlook some of the behind-the-scenes consequences of convenience. This post examines the cost of what some of those conveniences, how to change some default settings to lessen their impact, and what some situational alternatives can be.

What are Deploy Previews?

One of the core tenants of JAMstack is that a site builds, then it deploys. Netlify, like any other host for statically built sites, makes use of ”immutable deploys.”

While a “mutable” item can change (be mutated) over time, an “immutable” item cannot. Once created, an immutable deploy of a website becomes an artifact which will not change. Instead, deploys result in new versions or instances of the site, and traffic is routed to them accordingly.

When paired with atomic deploys, immutable builds make it possible for sites to enjoy abilities such as instant rollbacks and versioning, and help to ensure that the code and assets of a website can be maintained in a known state.


A Preview Deploy is something special that Netlify adds to atomic deployment.

Deploy Previews work by deploying every pull request from your Git repository to a unique URL; completely different from the one your main site uses. You and your team can see how those changes look before they’re merged into the main branch and deployed to production.

Preview Deploys are useful because they let you test a new feature in a production-like environment, share a url to get feedback before a release, or easily cross-browser check a site.

The Deploy Preview settings for a site live at Settings > Deploys > Deploy Contexts, and the defaults look like this:

the default deploy settings for netlify - Deploy Previews: Any pull request against your production branch / Branch deploys: Deploy only the production branch

The different options for each setting look like this:

Deploy previews
Any pull request against your production branch / branch deploy branches
Netlify will generate a deploy preview with a unique URL for each built pull request.
Netlify won’t build deploy previews for any pull requests.
Branch deploys
Deploy all the branches pushed to the repository.
Deploy only the production branch.
Let me add individual branches
Deploy only the production branch and the following additional branches.

One interesting thing is that the Any pull request against your production branch option will still build a preview even for a draft pull request.

With Great Power… Comes Great Responsibility

I can’t find a source for the first person who said this, but it is a well known best practice to “commit small; commit often.” While this is generally the best way to use version control, this could cause a lot of builds using Netlify’s default settings.

This is a habit I picked up at my day job where I work on a team, but I usually open a pull request fairly soon after I create a new branch. I know most people probably don’t work this way, but I make heavy use of git’s rebase functionality and keeping an open PR is a good safety net when changing the commit history.

screenshot of a rebased and merged pr, https://github.com/ryanfiller/portfolio-svelte/pull/30

Any pull request regardless of open, closed, or merged status, keeps a history of commits that have been force pushed to it. This PR, merged months ago, still lets me access code from the commits 23555ed, d58c989, and 9022382. If you mess up during a rebase and push something you didn’t mean to, having an open PR can be a much easier way of recovering code than trying to fish it out of the local reflog.

Though once this PR is open, Netlify’s pipeline will become aware of it and will start building a preview deploy any time you push code.

Also, some tools, like dependabot, might automatically come with a starter project and keep making PRs after you’ve forgotten about a project.

Even Netlify’s own CMS might be creating deploys without you realizing it. NetlifyCMS is a git-based CMS, so of course it will be making commits as content is added or changed. But, even with the editorial_workflow option enabled, the CMS will open a draft PR and still create a deploy.

netlify cms editoral workflow draft post, github draft pr, and netlify preview build screenshots

None of this is meant to knock these tools at all! I’ve written in the past about how much I enjoy NetlifyCMS and I still use it for small freelance sites that need to be editable by clients. That point I’m trying to make is that it is important to know how a tool works when you use it. In this case creating a post will create a pull request, and then every subsequent save of that page will create a commit, and thus a deploy.

This is not something I always knew I needed to be careful about. One of my posts alone generated almost 40 separate deploys.

Some Downsides to “Single Use Deploys”

One thing that might make someone start paying attention to how many deploys they are creating is Build Minute usage. Netlify’s plans only offer so many minutes per month before the next tier of billing kicks in. I’m sure everyone wants to avoid incurring any unexpected costs for a site, but I wanted to look at the other cost - the deploys themselves.

Because of the nature of how Netlify’s roll backs work, deploys never go away.

When paired with atomic deploys, immutable builds make it possible for sites to enjoy abilities such as instant rollbacks and versioning, and help to ensure that the code and assets of a website can be maintained in a known state.


I reached out to Netlify about how exactly their deploy process works. Their customer service rep told me this was getting close to “technical secrets territory,” but was able to link me to this helpful blog post by Cassidy Williams. The section about what happens when you deploy a JAMstack project demonstrates (with gifs!) how the deploy process happens.

Netlify’s deploys work a little bit like git commits — each one builds on previous ones, which is why you’re able to easily “roll back” to a previous atomic state. In order to maintain the ability to always and instantly revert to a previous deploy, Netlify doesn’t let you remove them.

And this isn’t just production deploys, this applies to preview deploys as well. To be honest, I’m not sure why this is and I couldn’t find a definitive answer anywhere. It could be that these deploys are cached or reused in some way to support production builds, or it could just be that there is no meaningful technical distinction between build types in Netlify’s system.

As far as I can tell, unless you delete a site from Netlify altogether, once you create a deploy it exists forever. Here is the first draft of the first post for this blog from December 31, 2019 at 7:42am. It’s probably riddled with typos. Here is my first commit to my first Gatsby project while I was working through their tutorial, deployed January 23, 2018 at 10:02pm. Here is my very first deploy ever to Netlify, from September 16, 2017 at 1:54pm, just called “refactor.”

It might seem cool that these sites exist forever, but ask yourself how often you really need to go back and look at the state of a site from over a year ago? Or even a few months ago? Also, consider this tweet from Gerry McGovern -

the book cover of 'World Wide Waste'

Last year I read Gerry’s book, World Wide Waste. The gist of the book is to try and get everyone to consider the physical world ramifications of their digital world choices. One series of facts really stood out to me -

  • Around 90% of data is never accessed three months after it is first stored.
  • 80% of all digital data is never accessed or used again after it is stored.
  • Businesses typically only analyze around 10% of the data they collect.
  • 90% of unstructured data is never analyzed.
  • 90% of all sensor data collected from Internet of Things devices is never used.
from Gerry McGovern’s website

For a lot of people, myself sometimes included, these Deploy Previews are artifacts that are never even looked at.

Estimating the Cost of This Site

After seeing Gerry’s tweet I wanted to try to calculate a number for the amount of CO2 my blog has generated via hosting my deploys. I really want to stress that this is a ballpark number. I’m not an expert on things like compression and bandwidth and I’m working with a lot of assumptions and averages. Hopefully I’ll get fairly close.

The first thing I did is determine how big each deploy is. This is kind of a tough number to get for a few reasons. First, the site gets bigger as I add more content and features to it, so I know I’ll be looking at some kind of average. Second, roughly halfway between relaunching in 2020 and now I changed frameworks from Gatsby to Sapper which definitely changed the overall bundle size of my site.

I looked around in Netlify to see if they had any info on storage size but the closest metric I could find was bandwidth used per month. Again, I’m not an expert in this, so the best way I could figure out the size of a site was to use SiteSucker to download it and check its size on my local disk. After getting a rough estimate for month and averaging them, these are the numbers I got.

Gatsby Sapper
Average Size 79759.73 kb (0.08 gb) 33000.00 kb (0.03 gb)

I use Netlify Large Media to handle hosting media assets and those should be versioned outside of site deploys and then only pulled in by url reference. However, for the demonstration here I’m going to leave them in the calculation.

Now that I knew about how big each deploy is, the next step was to figure out how many times the site has deployed. This number was pretty easy to get. For each project I went to the Project > Deploys tab, counted the number of deploys per page then multiplied by the number of pages. I did this both for my Production and Preview deploys since neither can be removed.

Gatsby Sapper
Preview Deploys 73 205
Total Deploys 319 261

The extremely large number of Sapper deploys vs Gatsby deploys seems to be because I was working with a feature I needed to test against a bot crawler, and this wouldn’t work running the site locally.

To get a final number for each, I multiplied these totals by the average size of each deploy.

Gatsby Sapper Total
Average Size * Preview Deploys 5.84 gb 6.15 gb 11.99 gb
Average Size * Total Deploys 25.52 gb 7.83 gb 33.35 gb

If I were to hypothetically stop working on my site and leave it with its current number of deploys for an entire year, that means I can take this total number and multiply it by Gerry’s calculation of 30 grams per 1gb of stored data. That comes out to 910.5 grams, or just over two pounds. Based on this random nasa.gov link I found, that’s about one 10th of a gallon of burned gasoline.

About a third of that overall number comes from Deploy Previews, and I want to reference two of the numbers from World Wide Waste again -

  • Around 90% of data is never accessed three months after it is first stored.
  • 80% of all digital data is never accessed or used again after it is stored.

These are deploys I simply do not need to keep forever. And because these deploys are out there and can’t be removed, they’ll compound this storage cost every year that they continue to exist. There is an argument to be made that a lot of those Production Deploys aren’t needed either, but theoretically Netlify should keep them around in case I ever need to roll back to one of them.

This might seem insignificant, but keep in mind this is only one project of mine. Granted I don’t actively work on them nearly as much as I do my blog, but I currently have two dozen sites hosted on Netlify. Multiply this across Netlify’s over 1,000,000 devs and the scope is anything but insignificant.

If every Netlify user has only one site that they have deployed as much as this blog, that means the equivalent 100,000 gallons of gasoline burned just for one year of storage alone. According to epa.gov, that’s the same as driving 2,233,479 miles in a car in the same amount of time. To offset this amount of C02 you would need to plant 14,695 trees and let them grow for ten years!

What Can You Do?

I’m certainly not suggesting that anybody not use Netlify. On the other hand, I have made some changes to how and why I use Netlify by tweaking my Deploy Settings.

First, I have Deploy Previews set to Don’t deploy pull requests. This makes sure that my “make a PR as a rebase safety net” workflow doesn’t create deploys, and neither do things like dependabot. Second, instead of Branch Deploys set to Deploy only the production branch I use Let me add individual branches where I set up a dedicated staging branch that I can merge into any time I want to manually generate a preview.

my deploy settings for netlify - Deploy Previews: Don't deploy pull requests / Branch deploys: staging

I’ve also made a conscious effort to only use my staging branch when I’m trying out a new feature that needs to be tested in as close to the actual production environment as possible. For things like getting a blog post proofread or trying to do a quick check of some styles on a mobile device I’ve switched over to using surge.sh.


surge.sh logo, its a walrus
the surge.sh logo

surge.sh is not a new tool. It, along with CodeShip, is what I used to use to host and deploy my site before I moved to using Netlify. surge.sh and Netlify are similar in some ways, with surge.sh being a more stripped down version of the same type of static hosting service. After installing the surge cli, any directory can be instantly deployed to a surge.sh url.

The CLI accepts a number of configuration flags for options such as which directory to publish and which url to publish use. To make this all a little more automatic, I added a surge script to the scripts section of my package.json file.

"surge": "npm run export && surge --project ./__sapper__/export --domain beta.ryanfiller.com"

This lets me run npm run surge which will export the static version of my site and push it to the subdomain beta.ryanfiller.com.

One nice thing about this is that by pushing to the same url over and over again, I can overwrite the version of the site that was previously hosted there. I like this because it means that when I link someone to a preview of a blog post they don’t have to sift through different versions of hashed urls. They can visit the beta subdomain and see the draft that I most recently pushed.

I reached out about how surge.sh works behind the scenes, and they were actually nice enough to let me in on a semi-secret upcoming version of the CLI. This version was described as “unreleased but actually quite stable”, and I’ve had no issues using it. A preview of it can be found (at the time of me writing this, anyways) by bumping your version up to the current release candidate.

npm install -g surge@edge

After this version of surge is installed you can run surge --help and see a list of all the new available commands. There’s a ton of good stuff here, but I wanted to highlight a few that are relevant to the topic of website size.

$ surge -h

  Surge.sh ⚡ Static Web Publishing 0.24.0-rc.6

  surge list     <domain>                            list all project revisions
  surge discard  <revision>                          remove revision from system
  surge audit    <domain>                            audit edgenode state
  surge teardown <domain>                            tear down a published project

To be honest, until seeing this new list of commands and running some of them, I had a pretty big misconception about how surge.sh actually works. I was under the impression that when you push to an existing url the old site was gone and replaced with the new one. Running surge list against some of my old deploys actually showed me that every version of them was still around, similar to Netlify. The big win here, to me, is being able to run surge discard against a particular deploy and remove it once you’re done with it. And of course, if you want to remove a project entirely you can run surge teardown. surge audit was also super cool, it shows a list of deploys with their unique id, total file count, and how many megabytes of space they’re taking up on the surge.sh servers.

$ surge audit beta.ryanfiller.com

   sfo-15    1619787001755    255 files    55.35 MB
   sjc-00    1619787001755    255 files    55.35 MB
   jfk-01    1619787001755    255 files    55.35 MB

This makes surge.sh an awesome tool, and one that puts more power back in your hands as far as managing what artifacts from your site persist forever. Netlify is a pretty big ecosystem at this point, with SSL, Large Media, Functions, Auth, Analytics, and so much else, but for some simple projects in the future I think I’m going to look at hosting them solely on surge.sh

My Netlify Wish List

This isn’t meant to be a list of demands, only a list of things I’d do if I had some magic wishes and could make changes. I’m sure that some of these are a lot more technically challenging than they seem on the surface. In no particular order:

Don’t Automatically Deploy Draft PRs

GitHub added draft pull requests pretty recently, but it does seem like this data is available in their REST API. It looks like GitLab also already supports draft merge requests and Bitbucket is working on a similar feature.

Maybe in the future Netlify could add an additional configuration knob to decide whether or not a draft should create a build?

This would also solve the issue where NetlifyCMS accidentally creates a deploy on every save of the page.

Add More Information to Outgoing Webhooks

I didn’t even touch on this in this post, but it would be nice to customize Deploy Notifications and Build Hooks a little more. My main blog uses these to syndicate posts to another domain, and builds of the secondary site are currently triggered by both Production and Previews Deploys of the main site.

There are already a lot of options around Deploy Preview in Settings > Deploys > Deploy Notifications, maybe somehow loop these into the Outgoing Webhook option?

Change The Default Settings

I know that within the last few weeks Netlify has announced a cool, new feature for Deploy Previews. Honestly, I’m kind of jealous that such an integrated tool didn’t exist when I worked at an agency and needed to gather client feedback. But, especially since, as of now, every deploy is forever, is an automatic deploy for every PR something that every site needs turned on by default?

Previews are a useful feature for a lot of people, but I’d love to know if the number of deploys created that were never even looked at would go down if this was an opt-in rather than opt-out setting.

Let Me Remove Deploy Previews

I know this one is a stretch, but I would love to be able to remove all of the one-off previews of my sites that I either didn’t mean to make or don’t need anymore. I have a feeling that of all my list this is the most technically complex — I understand that granting users this power makes the stack of deploys a bit like a game of Jenga where you hope you’re not removing a deploy you might need to roll back to later.

Some Final Thoughts on Netlify

Incremental Static Regeneration is an up-and-coming Netlify feature, and is the actual topic of Cassidy’s article that I linked to earlier. Jason Lengstorf and Phil Hawksworth were recently guests on two podcasts that I listen to (ShopTalk Show #464 and Toolsday #130) to talk about this feature. ISR is mostly unrelated to the topic of this post, but in both podcast episodes the phrase “burn the electricity to build a bunch of rarely visited pages” was used and it caught my attention both times. This post isn’t meant to disparage Deploy Previews in any way. It sounds like Netlify already has put some thought into servers doing needless work during the build process, I would love to see them adopt a similar train of thought about “burning electricity” with hosting as well.

Acknowledgments and Disclaimers

Thank you to Gerry McGovern for writing both the book and the tweet that inspired this post. World Wide Waste really has changed how I approach most everything I do online. Also, thank you to Scott Parker and Brock Whitten from Netlify and surge.sh respectively for doing their best to answer my cryptic “hey, but how does your product really work?” emails. The resources they provided me with helped to fill in a lot of details I was missing about how these technologies work.

I’m wouldn’t be surprised there’s holes in my math or overall logic. There’s probably some type of server-side compression or way to use gzip I’m not accounting for. Or I didn’t factor in how Large Media works correctly. I also didn’t account for redundancy with Netlify’s CDN or the fact that some of these servers are powered by renewable energy. This wasn’t meant to be a hard breakdown of the numbers, just some quick math to show how much things we tend to not think about can add up at scale.

If you have questions, comments, or concerns about the numbers I showed and how I came up with them, please let me know! Math is not always my strong suit and I’d love to update this post with more correct information.

If this post was meaningful to you in any way, share it! I really want to emphasize that my goal isn’t to shame anyone for the tools they use, but instead to get more people to think through the long-term consequences of their short-term actions, especially online in digital spaces.