Smaller Next.js images! (standalone support)

Images for Next.js apps from generated Dockerfiles using fly launch will be dramatically smaller (read: ~400MB smaller) when using the standalone output option in your next.config.js

Read the full documentation here: Run a Next.js App · Fly Docs

For new Next.js apps:
Ensure that your next.config.js file contains output: "standalone"

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone"
};

export default nextConfig;

Then run fly launch, and the appropriate Dockerfile will be generated.

For existing Next.js apps deployed to Fly.io:
Inside your Dockerfile, after running the build script (npm run build), there should typically be a final COPY instruction towards the end of one’s Dockerfile. Replace that with the following:

COPY --from=build /app/.next/standalone /app
COPY --from=build /app/.next/static /app/.next/static
COPY --from=build /app/public /app/public

Lastly, update your CMD to the following:

CMD [ "node", "server.js" ]
3 Likes

Access Log Hack
If you are using the above with NextJS version 14.1, you can use the follow hack to create access logs:

  1. Add a file to your repository called logging.js that looks something like this:
/* eslint-disable no-console */
/**
 * Log to console each request.
 * @param { import('http').IncomingMessage } req
 * @param { import('http').ServerResponse } res
 */
function log(req, res, requestTime) {
  const user_agent = req.headers['user-agent'];
  console.log(
    JSON.stringify({
      request_method: req.method,
      request_uri: req.url,
      request_time_ms: Number(requestTime.toFixed(1)),
      content_type: req.headers.content_type,
      user_agent,
      status: res.statusCode,
      reqId: req.headers['fly-request-id'],
      x_forwarded_for: req.headers['x-forwarded-for'],
    }),
  );
}

module.exports = log;
  1. Add two commands to your Dockerfile, after the COPY of /app/public something like:
COPY logging.js /app/logging.js

# Inject logging into next's server code
RUN sed -i -e '/"use strict";/a\const log = require("/app/logging")' \
    -e '/await requestHandler(req, res)/s/^/    const startTime = performance.now()\n/' \
    -e '/await requestHandler(req, res)/a\    const requestTime = performance.now() - startTime\n    log(req,res,requestTime)\n' \
    /app/node_modules/next/dist/server/lib/start-server.js

And you will have access logs with request times!