One cache for all instances

Point every instance at one shared cache so revalidations reach all.

OpenNextopen-next.js.org
OpenNext adapts Next.js for AWS Lambda, Cloudflare Workers, etc.
Serverless without Vercelopen-next.js.org ↗

From next-best-practices, self-hosting.md.

Two instances, one balancer

Revalidate on A, then send 10 users.

Filesystem (default)Bad
Load balancer
10 users

    .next/cache per instance
    Shared Redis handlerGood
    Load balancer
    10 users

      cacheHandlers → Redis, cacheMaxMemorySize: 0
      // next.config.js
      module.exports = {
        output: 'standalone',
        cacheMaxMemorySize: 0,
        cacheHandlers: {
          default: require.resolve('./cache-handler.js'),
        },
      };
      
      // cache-handler.js
      const Redis = require('ioredis');
      const redis = new Redis(process.env.REDIS_URL);
      const CACHE_PREFIX = 'nextjs:';
      
      module.exports = class CacheHandler {
        async get(key) {
          const data = await redis.get(CACHE_PREFIX + key);
          if (!data) return null;
          const parsed = JSON.parse(data);
          return { value: parsed.value, lastModified: parsed.lastModified };
        }
      
        async set(key, data, ctx) {
          const cacheData = JSON.stringify({ value: data, lastModified: Date.now() });
          if (ctx?.revalidate) {
            await redis.setex(CACHE_PREFIX + key, ctx.revalidate, cacheData);
          } else {
            await redis.set(CACHE_PREFIX + key, cacheData);
          }
        }
      
        async revalidateTag(tags) {
          // not in the skill's example: map tags to keys yourself
        }
      };

      Replay the verification script

      Run the script, or step one command at a time.

      Filesystem cacheBad

        Redis cache handlerGood

          PORT=3001 node .next/standalone/server.js &
          PORT=3002 node .next/standalone/server.js &
          curl http://localhost:3001/api/revalidate?path=/posts
          curl http://localhost:3001/posts
          curl http://localhost:3002/posts

          What a second instance breaks

          Switch the deployment.

          Deployment

          FeatureStatus
          # Dockerfile
          COPY --from=builder /app/.next/standalone ./
          COPY --from=builder /app/.next/static ./.next/static
          COPY --from=builder /app/public ./public
          ENV HOSTNAME="0.0.0.0"
          CMD ["node", "server.js"]
          
          // app/api/health/route.ts
          export async function GET() {
            return Response.json({ status: 'healthy' }, { status: 200 });
          }

          Why it matters, pros and cons

          Why it matters

          The default cache lives on each disk, so a revalidation reaches one instance.

          Pros

          • Every user sees the same page
          • New instances start warm
          • Cache survives container redeploys

          Cons

          • Pointless with one instance or managed hosting
          • Every cache read becomes a network hop
          • Handler API changes between majors; retest on upgrade