From 6592916d1fc8c494b10006b465424ca06af8a0db Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Mon, 10 Mar 2025 22:25:19 -0700
Subject: [PATCH 1/7] blog: r2 migration blog post
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
apps/site/authors.json | 5 +
.../making-nodejs-downloads-reliable.md | 221 ++++++++++++++++++
.../announcements/old-release-asset-infra.png | Bin 0 -> 51299 bytes
3 files changed, 226 insertions(+)
create mode 100644 apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
create mode 100755 apps/site/public/static/images/blog/announcements/old-release-asset-infra.png
diff --git a/apps/site/authors.json b/apps/site/authors.json
index 85019ae535ef8..f22fcb8ccbaa0 100644
--- a/apps/site/authors.json
+++ b/apps/site/authors.json
@@ -78,6 +78,11 @@
"name": "Evan Lucas",
"website": "https://github.com/evanlucas"
},
+ "flakey5": {
+ "id": "flakey5",
+ "name": "flakey5",
+ "website": "https://github.com/flakey5"
+ },
"Gibson Fahnestock": {
"id": "gibfahn",
"name": "Gibson Fahnestock",
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
new file mode 100644
index 0000000000000..e01d4bb3225ac
--- /dev/null
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -0,0 +1,221 @@
+---
+date: '2024-12-28T12:00:00.000Z'
+category: announcements
+title: Making Node.js Downloads Reliable
+layout: blog-post
+author: flakey5
+---
+
+Last year, we shared [the details behind Node.js' brand new website](https://nodejs.org/en/blog/announcements/diving-into-the-nodejs-website-redesign).
+Today we're back, talking about the new infrastructure serving Node.js' release assets.
+
+
+
+This blog post goes into what Node.js' web infrastructure looks like, its history, and where it stands today.
+We're also going to be covering what exactly we had in mind and were prioritizing with this infrastructure overhaul.
+
+## Some Brief History
+
+At the start of the project in 2009, Node.js release assets (binaries and documentation) were stored on a publicly accessible S3 bucket.
+This was later changed around May of 2010 to a VPS, which also hosted the Node.js website.
+
+When the io.js fork happened in 2014, they also had used a VPS to host the website and release assets.
+After the Node.js and io.js merge in 2015, io.js' VPS (which will be referred to as the origin server from hereon) was repurposed to host both io.js and Node.js releases, along with the Node.js website.
+
+A backup server was also created, which acted almost like an exact copy of the origin server.
+It served two main purposes:
+
+1. Serve any traffic that the origin server couldn't handle
+2. Serve as a backup for the binaries and documentation in case if something went wrong with the origin server.
+
+The entire architecture looked like this:
+
+
+
+### How Node.js Releases are Released
+
+When a mainline release is ready to be released, the releaser triggers a release CI job.
+These jobs build the Node.js binaries for all supported platforms and generate the documentation files.
+
+Upon a release CI job completing successfully, its assets are uploaded to the origin server in a staging directory.
+When all of the release CI jobs finish, the releaser then manually "promotes" the assets to the production directory, making the release publicly accessible.
+
+For nightly or test releases, the promotion happens automatically.
+
+_Refs: [Release Overview](https://github.com/nodejs/build/blob/main/doc/release-overview.md), [Node.js release process](https://github.com/nodejs/node/blob/main/doc/contributing/releases.md)_.
+
+## Growing Pains
+
+Nowadays, the nodejs.org domain sees over 3 billion requests and 2+ petabytes of traffic per month, with the majority of that going towards release assets.
+
+
+
+This averages to about 1,157 requests per second, with an average bandwidth of 771 mb per second.
+
+
+ Math
+
+3,000,000,000 requests per month / 30 days / 24 hours / 60 minutes / 60 seconds = ~1157 requests/second.
+2,000,000,000 mb per month / 30 days / 24 hours / 60 minutes / 60 seconds = 771 mb/second.
+
+
+The origin server does not have enough resources for this, and it struggled to keep up with the demand.
+The nodejs.org website was moved off of the origin server as part of the redesign which did help, but it still wasn't enough.
+
+Caching was being used rather inefficiently as well.
+Node.js makes heavy use of symlinks with its release assets.
+Some of these symlinks include directories like `https://nodejs.org/dist/latest/`, which changes with each release.
+When a release is promoted, it needs to clear the cache in order to update these symlinks so users actually get the latest version.
+However, there wasn't a great way to clear only what we needed to clear from the cache.
+This resulted in us needing to purge the _entire_ cache with each release.
+Node.js has nightly releases, meaning this was happening once every 24 hours _at a minimum_.
+So, everyday at roughly midnight UTC, the origin server got effectively DDoS'ed from the amount of uncached traffic being sent to it.
+
+There were also a handful of other issues with the origin server pertaining to its maintenance:
+
+- Documentation of things running on the server was spotty; some things were well documented and others not at all
+- Changes performed in the [nodejs/build](https://github.com/nodejs/build) repository needed to be deployed manually by a Build WG member with access. There was also no guarantee that what's in the build repository is what's actually on the server.
+- There's no staging environment other than a backup instance.
+- Rollbacks could only be done via disk images through the VPS providers' web portal.
+
+## Attempts Were Made
+
+All of these issues combined created for scenarios where the origin server wasn't touched unless necessary, including a period in which it had over _3 years of uptime_.
+These factors also contributed to incidents such as the one that occurred from [March 15th, 2023 to March 17th, 2023](/en/blog/announcements/node-js-march-17-incident), where the Node.js release assets were unavailable for 2 days due to the origin server being overloaded and improper caching rules.
+Between incidents like this and the daily outages that were occuring, users were being effected and were painfully aware of the unreliability of the infrastructure.
+
+This needed to be fixed.
+
+However, attempts made to remediate these issues in this infrastructure could only go so far.
+The NGINX configuration was changed to modify its resource consumption and add in missing documentation.
+Cloudflare WAF was integrated to block spammy requests from repositories such as Artifactory.
+Load balancing changes were made to try to help lessen the load.
+But, these ultimately weren't as effective as they needed to be.
+
+## The Rewrite
+
+In August of 2023, [Claudio Wunder](https://github.com/ovflowd) and [I (flakey5)](https://github.com/flakey5) started working on a proof-of-concept for a new way to serve Node.js release assets.
+
+The idea was "simple": create a new service that solved all of the issues we had with the previous infrastructure and showed no noticeable difference from the old infrastructure to users.
+In order to meet these requirements, we prioritized three main goals with this new service:
+
+1. Reliabiliy: The service needs to be as close to 100% uptime as possible.
+2. Maintainability: Maintainers should not have to worry about things toppling over because they changed something. The service needs to be well-documented and as clean and simple as possible.
+3. Efficiency: Whatever platform was used, the service would need to make full use of it to provide the best performance and experience as possible.
+
+In order to meet these requirements, we ended up using [Cloudflare Workers](https://developers.cloudflare.com/workers) and [R2](https://developers.cloudflare.com/r2) for a handful of reasons:
+
+1. Workers and R2 are, and historically have been, reliable and fast.
+2. Workers takes care of the infrastructure for us, so we just need to maintain the service itself. This heavily lessens the cost of maintenance, especially for a team of volunteers.
+3. Node.js had already had previous usage of Cloudflare services; it makes sense to look into expanding it.
+4. Pricing. Cloudflare was gracious enough to provide Node.js with free access to Workers and R2 under [Project Alexandria](https://www.cloudflare.com/lp/project-alexandria).
+
+In September of 2023, the proof-of-concept was ready to be reviewed, and an issue was made in the nodejs/build repository ([#3461](https://github.com/nodejs/build/issues/3461)) seeking approval from the Build WG.
+
+After the Build WG discussed the change, it was approved and we started working on getting the service, which is now referred to as the Release Worker, deployed to production.
+
+## The Journey to Production
+
+With developing the Release Worker came a lot of trial and error and learning over numerous iterations of the service.
+It needed to have all of the same features and similar, if not the exact same, behaviors as its predecessor, NGINX.
+
+### What It Needed To Do
+
+For starters, it needed to be able to provide the latest releases of Node.js as soon as soon as they're released.
+
+Secondly, it needed to handle routing correctly.
+Most assets don't have 1:1 mappings of their URL to where they are located in the file system.
+Where a URL maps to can even change depending on the Node.js version that's being requested.
+For instance,
+
+| URL | File Path |
+| --------------------------------- | -------------------------------------- |
+| `/dist/index.json` | `nodejs/release/index.json` |
+| `/dist/latest-iron/...` | `nodejs/release/v20.x.x/...` |
+| `/docs/v0.1.20/...` | `nodejs/docs/v0.1.20/...` |
+| `/docs/v22.0.0/...` | `nodejs/release/v22.0.0/docs/api/...` |
+| `/dist/v0.4.9/node-v0.4.9.tar.gz` | `nodejs/release/node-v0.4.9.tar.gz` |
+| `/dist/v0.4.9/SHASUMS256.txt` | `nodejs/release/v0.4.9/SHASUMS256.txt` |
+
+This behavior was created from multiple different changes throughout the release cycle and the way release assets were distributed, and was achieved through symlinks.
+However, R2 doesn't support symlinks, meaning we needed to come up with a solution on our own.
+
+Finally, we needed to meet the reliability goal.
+To do this, we implemented four things:
+
+1. Any request to R2 that fails is retried 3 times (in additon to the retries that Workers already performs).
+2. A "fallback" system. Any request to R2 that fails all retries is rewritten to the old infrastructure.
+3. When an error does happen, it's recorded in [Sentry](https://sentry.io/welcome) and we're notified so we can take appropriate action.
+4. Slack alerts are in place for Sentry and for any critical point of failure in the Release Worker (ex/ deployment failure)
+
+### The Iterations
+
+We first started off with an incredibly simple worker.
+Given a path in the URL, it checked if the file requested existed in the R2 bucket.
+If it did, it returned it.
+Otherwise, it rewrote the request back to the origin server.
+For requests that resulted in a directory listing, the worker just forwarded those over to the origin server.
+This iteration obviously didn't cover nearly any of the requirements, so it was back to the drawing board.
+
+The second iteration was based off the popular R2 directory listing library [render2](https://www.npmjs.com/package/render2), developed by [Kotx](https://github.com/Kotx).
+The library worked well for the more generic use cases we needed to cover, however, it fell short in the more unique use cases that we had.
+So we forked it, adding what we needed for it to work for us.
+However, it became rather messy and thus fell short of our maintainability goal.
+
+This led us to our third iteration, which was a complete rewrite while still keeping some aspects of render2.
+It worked for the most part, but this too was also a mess and didn't meet our maintainability goal.
+It was also designed in the exact way that we needed the service to behave as well.
+If we needed to change that behavior in any way, we would need to refactor significant portions of the codebase.
+We knew we could do better than this.
+
+This led us to the fourth and current iteration of the Release Worker, which was yet again another rewrite.
+This time however, it was designed to be a lot more modular and with a middleware-centric design.
+This allowed for code that was a lot easier to keep track of and maintain, and, as of November 2024, is what is currently deployed to production.
+
+### Release Process Changes
+
+The goal with integrating R2 into the release process was to change as little as possible.
+To accomplish this, a similar workflow to what was already in place was made.
+
+Release CI jobs now upload their assets to a `dist-staging` bucket in addition to the staging directory on the origin server.
+Then, when a release is promoted, the assets in `dist-staging` are copied over to a `dist-prod` bucket, which is what the Release Worker reads from.
+
+## Maintainability
+
+As said in our previous blog post on the website redesign, an open source project is only as good as its documentation.
+In order for the Release Worker to be maintainable, it needed to well documented.
+
+This was achieved not only through thorough comments in the codebase but also documents such as,
+
+- [README](https://github.com/nodejs/release-cloudflare-worker/tree/main/README.md)
+- [Collaborator Guide](https://github.com/nodejs/release-cloudflare-worker/tree/main/COLLABORATOR_GUIDE.md)
+- [Contributing Guide](https://github.com/nodejs/release-cloudflare-worker/tree/main/CONTRIBUTING.md)
+- [General Documentation](https://github.com/nodejs/release-cloudflare-worker/blob/main/docs) on things
+- [Standard Operating Procedures](https://github.com/nodejs/release-cloudflare-worker/blob/main/docs/sops) for things such as incident flows.
+
+## What's next?
+
+The work isn't done _just_ yet.
+We still want to,
+
+- Look into any performance improvements that could be made.
+ - This includes looking into integrating [Cloudflare KV](https://developers.cloudflare.com/kv/) for directory listings.
+- Have better tests and a better development environment ([PR!](https://github.com/nodejs/release-cloudflare-worker/pull/252))
+- Metrics to give us more visibility into how the Release Worker is behaving and if there's anything that we can improve.
+
+## Thanks
+
+Many people and organizations have contributed to this effort in many different ways.
+We'd like to thank:
+
+- All of the [contributors and collaborators](https://github.com/nodejs/release-cloudflare-worker/graphs/contributors) that make this project possible.
+- [Cloudflare](https://cloudflare.com) for providing the infrastructure that serves Node.js' Website and the Release Worker. Also specifically to the R2 team for the technical support they have given us.
+- [Sentry](https://sentry.io/welcome/) for providing an open source license for their error reporting, monitoring, and diagnostic tools.
+- [OpenJS Foundation](https://openjsf.org) for their support and guidance.
+
+> Here is your weekly reminder that the Node.js project is driven by volunteers. Therefore every feature that lands is because someone spent time (or money) to make it happen. This is called Open Governance.
+> [Matteo Collina, via social media](https://x.com/matteocollina/status/1770496526424351205?s=46&t=22eoAstJVk5l46KQXYEk5Q)
+
+Want to get involved? [Check out the project on GitHub](https://github.com/nodejs/release-cloudflare-worker).
+
+> Fun fact! Did you know that Node.js has [a status page](https://status.nodejs.org)? No? You're not alone! We've been rather bad at using it to communicate these issues to users, but we're working on improving that.
diff --git a/apps/site/public/static/images/blog/announcements/old-release-asset-infra.png b/apps/site/public/static/images/blog/announcements/old-release-asset-infra.png
new file mode 100755
index 0000000000000000000000000000000000000000..03c3d869c790520a42a9ce8910990b0f4b20345e
GIT binary patch
literal 51299
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
Date: Fri, 28 Mar 2025 13:14:27 -0700
Subject: [PATCH 2/7] cleanup
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../blog/announcements/making-nodejs-downloads-reliable.md | 5 -----
1 file changed, 5 deletions(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index e01d4bb3225ac..f92c61eb97ee1 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -9,8 +9,6 @@ author: flakey5
Last year, we shared [the details behind Node.js' brand new website](https://nodejs.org/en/blog/announcements/diving-into-the-nodejs-website-redesign).
Today we're back, talking about the new infrastructure serving Node.js' release assets.
-
-
This blog post goes into what Node.js' web infrastructure looks like, its history, and where it stands today.
We're also going to be covering what exactly we had in mind and were prioritizing with this infrastructure overhaul.
@@ -48,13 +46,10 @@ _Refs: [Release Overview](https://github.com/nodejs/build/blob/main/doc/release-
Nowadays, the nodejs.org domain sees over 3 billion requests and 2+ petabytes of traffic per month, with the majority of that going towards release assets.
-
-
This averages to about 1,157 requests per second, with an average bandwidth of 771 mb per second.
Math
-
3,000,000,000 requests per month / 30 days / 24 hours / 60 minutes / 60 seconds = ~1157 requests/second.
2,000,000,000 mb per month / 30 days / 24 hours / 60 minutes / 60 seconds = 771 mb/second.
From e7ef7d02a01eb597fcdfe069ccbafaf3e32f275c Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Fri, 28 Mar 2025 14:51:15 -0700
Subject: [PATCH 3/7] update stats
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../announcements/making-nodejs-downloads-reliable.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index f92c61eb97ee1..e3e8115e3850a 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -44,14 +44,14 @@ _Refs: [Release Overview](https://github.com/nodejs/build/blob/main/doc/release-
## Growing Pains
-Nowadays, the nodejs.org domain sees over 3 billion requests and 2+ petabytes of traffic per month, with the majority of that going towards release assets.
+Nowadays, the nodejs.org domain sees over 2.4 billion requests and 3+ petabytes of traffic per month, with a majority of that going towards release assets.
-This averages to about 1,157 requests per second, with an average bandwidth of 771 mb per second.
+This averages to about 925 requests per second and an average bandwidth of 1.2 gb per second.
- Math
-3,000,000,000 requests per month / 30 days / 24 hours / 60 minutes / 60 seconds = ~1157 requests/second.
-2,000,000,000 mb per month / 30 days / 24 hours / 60 minutes / 60 seconds = 771 mb/second.
+ Math
+2,400,000,000 requests per month / 30 days per month / 24 hours per day / 60 minutes per hour / 60 seconds per minute = ~925 requests/second.
+3,000,000,000 mb per month / 30 days per month / 24 hours per day / 60 minutes per hour / 60 seconds per minute = ~1157 mb/second.
The origin server does not have enough resources for this, and it struggled to keep up with the demand.
From 896abbc654ff700793f5d082a832692ddef23f42 Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Fri, 28 Mar 2025 18:38:22 -0700
Subject: [PATCH 4/7] Apply suggestions from code review
Co-authored-by: Aviv Keller
Co-authored-by: Antoine du Hamel
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../announcements/making-nodejs-downloads-reliable.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index e3e8115e3850a..c2012ea3d139e 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -6,7 +6,7 @@ layout: blog-post
author: flakey5
---
-Last year, we shared [the details behind Node.js' brand new website](https://nodejs.org/en/blog/announcements/diving-into-the-nodejs-website-redesign).
+Last year, we shared [the details behind Node.js's brand new website](https://nodejs.org/en/blog/announcements/diving-into-the-nodejs-website-redesign).
Today we're back, talking about the new infrastructure serving Node.js' release assets.
This blog post goes into what Node.js' web infrastructure looks like, its history, and where it stands today.
@@ -23,7 +23,7 @@ After the Node.js and io.js merge in 2015, io.js' VPS (which will be referred to
A backup server was also created, which acted almost like an exact copy of the origin server.
It served two main purposes:
-1. Serve any traffic that the origin server couldn't handle
+1. Serve any traffic that the origin server couldn't handle.
2. Serve as a backup for the binaries and documentation in case if something went wrong with the origin server.
The entire architecture looked like this:
@@ -68,7 +68,7 @@ So, everyday at roughly midnight UTC, the origin server got effectively DDoS'ed
There were also a handful of other issues with the origin server pertaining to its maintenance:
-- Documentation of things running on the server was spotty; some things were well documented and others not at all
+- Documentation of things running on the server was spotty; some things were well documented and others not at all.
- Changes performed in the [nodejs/build](https://github.com/nodejs/build) repository needed to be deployed manually by a Build WG member with access. There was also no guarantee that what's in the build repository is what's actually on the server.
- There's no staging environment other than a backup instance.
- Rollbacks could only be done via disk images through the VPS providers' web portal.
@@ -141,7 +141,7 @@ To do this, we implemented four things:
1. Any request to R2 that fails is retried 3 times (in additon to the retries that Workers already performs).
2. A "fallback" system. Any request to R2 that fails all retries is rewritten to the old infrastructure.
3. When an error does happen, it's recorded in [Sentry](https://sentry.io/welcome) and we're notified so we can take appropriate action.
-4. Slack alerts are in place for Sentry and for any critical point of failure in the Release Worker (ex/ deployment failure)
+4. Slack alerts are in place for Sentry and for any critical point of failure in the Release Worker (ex/ deployment failure).
### The Iterations
@@ -195,7 +195,7 @@ We still want to,
- Look into any performance improvements that could be made.
- This includes looking into integrating [Cloudflare KV](https://developers.cloudflare.com/kv/) for directory listings.
-- Have better tests and a better development environment ([PR!](https://github.com/nodejs/release-cloudflare-worker/pull/252))
+- Have better tests and a better development environment ([PR!](https://github.com/nodejs/release-cloudflare-worker/pull/252)).
- Metrics to give us more visibility into how the Release Worker is behaving and if there's anything that we can improve.
## Thanks
From c1c7ef6ce1043ffb35c1f567a590aa30c585755c Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Thu, 3 Apr 2025 08:42:11 -0700
Subject: [PATCH 5/7] update release time
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../en/blog/announcements/making-nodejs-downloads-reliable.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index c2012ea3d139e..a4a9f1d05e744 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -1,5 +1,5 @@
---
-date: '2024-12-28T12:00:00.000Z'
+date: '2026-04-03T12:00:00.000Z'
category: announcements
title: Making Node.js Downloads Reliable
layout: blog-post
From c5353e6db0b07585b3cecca11fc329cbc1c4fd0f Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Thu, 3 Apr 2025 08:43:42 -0700
Subject: [PATCH 6/7] it's not 2026
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../en/blog/announcements/making-nodejs-downloads-reliable.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index a4a9f1d05e744..c1a35fdf7facb 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -1,5 +1,5 @@
---
-date: '2026-04-03T12:00:00.000Z'
+date: '2025-04-03T12:00:00.000Z'
category: announcements
title: Making Node.js Downloads Reliable
layout: blog-post
From 3ba707a044bb069dae6d9a843c108de2cb4aac6b Mon Sep 17 00:00:00 2001
From: flakey5 <73616808+flakey5@users.noreply.github.com>
Date: Sat, 5 Apr 2025 12:09:37 -0700
Subject: [PATCH 7/7] bump release date
Signed-off-by: flakey5 <73616808+flakey5@users.noreply.github.com>
---
.../en/blog/announcements/making-nodejs-downloads-reliable.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
index c1a35fdf7facb..e46fd70f16367 100644
--- a/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
+++ b/apps/site/pages/en/blog/announcements/making-nodejs-downloads-reliable.md
@@ -1,5 +1,5 @@
---
-date: '2025-04-03T12:00:00.000Z'
+date: '2025-04-05T12:00:00.000Z'
category: announcements
title: Making Node.js Downloads Reliable
layout: blog-post