Zero-downtime + Next.js not working

Hey!

I have a Next.js app hosted on render and every time I deploy the app is down for a minute or so while the app is building.

  • The only customization I’ve done is that I have mounted the disk /opt/render/project/src/.next/cache/images in order to cache image resizes between deploys
  • I’ve also implemented an /api/healthz but the app is unavailable before it gets called.

It might be the mounted disk that’s the issue - there’s nothing else in the logs around the time of the deploys just getting a blank page and a 5XX error.

Might be the mounted disk. Any other way of caching the images between deploys?

I can’t just static paths and prerender as some of the paths comes dynamically from an external service.

You’re correct. Attaching a persistent disk currently prevents zero-downtime deploys (Disks for Persistent File Storage | Render). We plan to remove this limitation, but in the meantime I think we can find a workaround.

Am I correct in assuming that the image resizing happens as part of the build process? If so, you may be able to leverage the build cache. Anything you store in XDG_CACHE_HOME (the cache directory) will persist between builds, unless you deliberately clear the cache with a “Clear build cache & deploy” or the total cache size exceeds 7GB.

You may also want to consider creating a Render static site that rewrites to your Next.js web service (see URL Redirects and Rewrites | Render). This would let you leverage our free global CDN and would prevent downtime, at least for static assets that are cached in the CDN.

I’m happy to elaborate on any of this if you have questions!

Hey David - they are not part of the build as most of them are dynamic paths + I don’t use next.js SSG as my app user SSR-auth so the pages that have static images doesn’t seem to be picked up. I also use api/-routes heavily, so Render’s static option is not an option.

However, there might indeed be a workaround –

Is it possible to write to XDG_CACHE_HOME / /opt/render/.cache from my app? If that’s properly persistent we could maybe do a symlink from /opt/render/project/src/.next/cache/images to /opt/render/.cache/next-images as part of the build?

Hi @KATT ,

XDG_CACHE_HOME is writable, but is only persisted between builds to help with caching, for example, package compilation. Whatever you put there at runtime will not be persisted.

To clarify @david 's suggestion to use a static site, you can use the rewrites/redirects feature so things like /api requests are rewritten to go to your dynamic site. In this solution, your static site acts as a proxy of sorts for your dynamic site.

1 Like

However it is not a static site, it’s a server rendered site where content may differ depending on the logged in user.

Yes, that makes sense. You can create a new static site in conjunction with your existing dynamic site if you’d like to follow @david’s suggestion. If you would prefer not to follow his suggestion, then you can avoid creating a static site.

@dan @david can you guys elaborate on this? I’d like to do something like you describe. Most of my site has dynamic paths although it is all generated on build and I’d like to serve them from a CDN.

Hi Miguel,

I’m not 100% on your use-case, but if you have a Static Site on Render they are deployed to a CDN. If your build process already produces fully static paths/pages, there’s nothing more you need to do, they’re on a CDN.

If those Static Sites require dynamic data from an API, one approach customers use is to add a rewrite rule as described above. For example, a rewrite like /api/*https://my-render-web-service-api.onrender.com/* allows the Static Site to have a dynamic path to an API, which can also help mitigate CORS issues, etc.

Hope that helps

Alan

Thanks for the reply Alan.

My issue is more with client side dynamic paths, eg. https://my-service.com/product/:productId. With React + React Router you can easily add a rule to rewrite everything to index.html, but with NextJS the router works differently and on build the static file is there in the path (something like) /product/[productId].html, and this works if you start from https://my-service.com/` but if you open the page https://my-service.com/product/:productId it will give you a 404 because can’t find that file. I was hoping there could be a rewrite like /product/* → https://my-render-web-service-api.onrender.com/product/* but then the [productId].html (and other similar files) won’t be cached in the CDN I guess, so a bit pointless. Am I wrong? Am I missing something?

I can try and deploy later today a quick demo on render to show as an example if you want.

Hi @al_ps, I had now time to deploy an example. So this is my question, I have a similar static website but with dynamic paths like this one: https://render-example-thth.onrender.com/, you can freely navigate back and forth to the posts and it works great. But when you try to open a post like https://render-example-thth.onrender.com/post/2 it gives a 404 because that path doesn’t exist (Nextjs just built a [id].html file).

So my idea was to have a Render Web Service with a Rewrite rule that any /post/* would go to the my-service.onrender.com/post/* but this won’t cache this [id].html files, isn’t it? So I believe my issue is still the same. I’m not sure how Vercel does it, but they own the stack so it is easier for them. Any ideas how I could achieve the same in Render?

Thanks in advance,
Miguel

P.S. Here’s the example repo in case it matters: GitHub - sircon/render-next-example

Hi Miguel,

You’re right, rewriting to another service won’t then cache that page on a static site.

I’m not overly familiar with Next.js, but I suppose some a couple of different approaches could be:

  • Generate the static pages for each post so they are fully static/cached. If you needed to trigger a new deploy when a post was added you can hit the Deploy Hook.

  • If the [id].html already contains the majority of the static assets for a post page, maybe you could use a rewrite for /post/*/post/[id].html to use that static page, and then hydrate the content from your API. So it’s partly cached and the dynamic content is fetched when needed.

Hope that helps

Alan

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.