# How to deploy Next.js applications with SSR and API routes

- Date: 2025-11-25T09:55:02.158Z
- Tags: Deployment
- URL: https://render.com/articles/how-to-deploy-next-js-applications-with-ssr-and-api-routes

Next.js applications present unique deployment considerations because they support multiple rendering strategies within a single framework. These include static generation, server-side rendering (SSR), API routes, and Incremental Static Regeneration (ISR)—which allows you to update static pages after deployment without rebuilding the entire site. Render provides flexible infrastructure that accommodates these different modes, but you'll need to understand how your Next.js architecture maps to Render's service types and configuration options.

This guide teaches deployment patterns through focused examples that illustrate core concepts. You'll learn to make informed decisions about service architecture, understand how rendering modes affect configuration, and recognize how Next.js-specific features interact with Render's infrastructure.

## Prerequisites and environment requirements

Before deploying Next.js applications to Render, verify your local environment meets these requirements:

- *Next.js Version*: [Next.js 16](https://nextjs.org) or later.
- *Node.js Version*: Node.js 20.x (Active LTS) or 22.x (Current LTS). Specify your required version explicitly using a `.node-version` file or `engines` field in `package.json` to ensure build consistency.
- *Repository Access*: Your Next.js application must be in a Git repository ([GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org/)) with Render authorized to access it.
- *Build Verification*: Run `npm run build` locally and confirm it completes without errors.
- *Environment Variable Inventory*: Document which environment variables your application requires at build time (prefixed with `NEXT_PUBLIC_`) versus runtime (used in API routes or `getServerSideProps` in Pages Router, or server components in App Router).

## App Router vs. Pages Router considerations

Next.js 13+ introduced the App Router, which changes how routing and data fetching work. This guide covers deployment principles that apply to both, but specific configuration patterns may vary:

- *App Router (recommended)*: Uses React Server Components by default. API routes are defined in `route.js` files.
- *Pages Router*: Uses `getServerSideProps` for SSR and `getStaticProps` for static generation. API routes are defined in `pages/api`.

Most Render configuration (service types, build commands, environment variables) remains consistent across both routers. The primary difference lies in how you implement data fetching and caching logic within your application code.

## Understanding Next.js deployment models on Render

You can deploy your Next.js applications on Render as either [Static Sites](https://render.com/docs/static-sites) or [Web Services](https://render.com/docs/web-services), depending on your application's rendering requirements.

```mermaid
flowchart TD
    A[Start: Next.js App] --> B{Uses API Routes\nor SSR?}
    B -- No --> C{output: 'export'\nin next.config.js?}
    B -- Yes --> F{output: 'standalone'\nin next.config.js?}
    C -- Yes --> E[Deploy as\nStatic Site]
    C -- No --> D[Deploy as\nWeb Service]
    F -- Yes --> G[Deploy as\nWeb Service (Standalone)]
    F -- No --> D

    style D fill:#d4e6ff,stroke:#333
    style G fill:#d4e6ff,stroke:#333
    style E fill:#e6fffa,stroke:#333
```

### Static Site Deployment

When your Next.js application uses only static generation and you've configured `output: 'export'` in `next.config.js`, you can deploy it as a static site. This model can't support SSR, ISR, or API routes because there's no Node.js server running after deployment.

### Web Service Deployment (Standard)

Applications using server-side rendering, ISR, API routes, or Next.js middleware require a persistent Node.js process. You'll deploy these as [Web Services](https://render.com/docs/web-services), executing `next start` to run Next.js's production server.

This simplified `render.yaml` demonstrates the minimal configuration for a Next.js application with SSR:

```yaml
services:
  - type: web
    name: nextjs-app
    runtime: node
    buildCommand: npm install && npm run build
    startCommand: npm start
    envVars:
      - key: NODE_ENV
        value: production
```

### Web Service Deployment (Standalone)

For optimized containerized or cloud deployments, Next.js offers `output: 'standalone'`. This creates a minimal folder at `.next/standalone` containing only the necessary files for production, significantly reducing the deployment size.

Enable it in `next.config.js`:

```javascript
module.exports = {
  output: "standalone",
};
```

Update your `render.yaml` start command:

```yaml
startCommand: node .next/standalone/server.js
```

*Note*: When using standalone mode, you may need to copy your `public` and `.next/static` folders to the standalone directory or configure your CDN to serve them, as the minimal server does not serve these by default. For most standard Render Web Service deployments, the default `next start` is sufficient and simpler.

## Static sites vs. SSR configuration

The `next build` command produces fundamentally different outputs depending on your configuration in `next.config.js`. This setting determines which Render service type you should use:

- *Static Export (`output: 'export'`)*: Generates a purely static site in the `out` directory (HTML/CSS/JS). This output contains no server-side code and requires only a CDN or static file server.
  - *Target Service*: [Static Site](https://render.com/docs/static-sites)
- *Standard Build (Default)*: Creates a `.next` directory containing both pre-rendered static pages and the Node.js server required for SSR, API routes, and ISR.
  - *Target Service*: [Web Service](https://render.com/docs/web-services)
- *Standalone Build (`output: 'standalone'`)*: Creates an optimized `.next/standalone` directory containing only the necessary files for production. This is ideal for reducing deployment size.
  - *Target Service*: [Web Service](https://render.com/docs/web-services) (with modified start command)

For a fully static Next.js site using `output: 'export'`:

```yaml
services:
  - type: static
    name: nextjs-static
    buildCommand: npm install && npm run build
    staticPublishPath: ./out
```

> <p><strong>Render Advantage: Zero-Downtime Deploys</strong></p>
> <p>Render's <a href="https://render.com/docs/web-services#zero-downtime-deploys">Zero-Downtime Deploys</a> ensure that your Next.js application remains available during updates. Render spins up a new instance of your service, waits for it to pass health checks, and only then switches traffic over from the old instance. This is critical for SSR apps where a restart would otherwise drop active requests.</p>

## Environment variables: build-time vs. runtime

Next.js's environment variable system distinguishes between build-time and runtime variable injection, affecting your Render configuration and security considerations.

- *Build-Time Variables*: Variables prefixed with `NEXT_PUBLIC_` get embedded into JavaScript bundles during `next build`. These values become part of your client-side code, visible to anyone who inspects your application.
- *Runtime Variables*: Variables used exclusively in API routes, `getServerSideProps`, or server-side configuration remain on the server and can safely contain secrets.

Configuration pattern in `render.yaml`:

```yaml
services:
  - type: web
    name: nextjs-app
    # ... other config ...
    healthCheckPath: /api/health
    envVars:
      - key: NEXT_PUBLIC_API_URL
        value: https://api.example.com
      - key: DATABASE_URL
        sync: false
      - key: API_SECRET_KEY
        sync: false
```

Note that `sync: false` prevents the value from being overwritten by the blueprint. You must set these sensitive values (like database passwords or API secrets) manually in the Render Dashboard, which is a security best practice.

### Configuring Health Checks

Render uses health checks to achieve zero-downtime deploys. Create a simple API route that returns a 200 OK status:

```javascript
// app/api/health/route.js
export async function GET() {
  return Response.json({ status: "ok" });
}
```

Configure `healthCheckPath` in your `render.yaml`:

```yaml
healthCheckPath: /api/health
```

Render will verify this endpoint is responsive before directing traffic to the new instance.

> <p><strong>Render Advantage: Native Secrets Management</strong></p>
> <p>Use <a href="https://render.com/docs/environment-groups">Environment Groups</a> to manage shared configuration across multiple services (e.g., your Next.js frontend and a Python worker). For sensitive files, use <a href="https://render.com/docs/secret-files">Secret Files</a> to securely inject certificates or keys at runtime without committing them to Git.</p>

## Configuring Next.js image optimization

[Next.js Image Optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) requires server-side processing and interacts with Render's infrastructure in specific ways.

The optimization process requires persistent disk for cache. Optimized images cache in `.next/cache/images`. On Render's Web Services, the default ephemeral filesystem means cached images are lost with each deploy or restart.

You can solve this problem with Render's [Persistent Disks](https://render.com/docs/disks):

```yaml
services:
  - type: web
    name: nextjs-with-images
    runtime: node
    buildCommand: npm install && npm run build
    startCommand: npm start
    disk:
      name: nextjs-cache
      mountPath: /opt/render/project/src/.next/cache
      sizeGB: 10
```

*External Image Source Configuration*: Configure `next.config.js` to allow external sources:

```javascript
module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "cdn.example.com",
      },
    ],
  },
};
```

## Handling incremental static regeneration (ISR)

[ISR](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) allows pages to update after deployment without full rebuilds.

When a request hits an ISR page after its revalidation period expires, Next.js serves the stale cached page immediately while triggering background regeneration. This process relies on writing to the filesystem (specifically `.next/cache`).

*Critical Requirement:* Because Render Web Services have ephemeral filesystems, the ISR cache will be lost on every deploy or restart unless you configure a *Persistent Disk*. Follow the same disk configuration pattern shown in the [Image Optimization](#configuring-nextjs-image-optimization) section above to mount a disk at `/opt/render/project/src/.next/cache`.

*On-Demand Revalidation*: Next.js supports programmatic page invalidation:

```javascript
// app/api/revalidate/route.js
import { revalidatePath } from "next/cache";
import { NextResponse } from "next/server";

export async function POST(request) {
  try {
    revalidatePath("/products/[id]");
    return NextResponse.json({ revalidated: true });
  } catch (err) {
    return NextResponse.json(
      { message: "Error revalidating" },
      { status: 500 }
    );
  }
}
```

## Example `next.config.js` configuration

Here is a complete `next.config.js` example combining the optimization and deployment features discussed:

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Optional: "standalone" reduces deployment size but requires handling static files manually
  // output: "standalone",

  // Configures image optimization for external sources
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "cdn.example.com",
        port: "",
        pathname: "/**",
      },
    ],
  },

  // Optional: Custom headers or redirects
  async redirects() {
    return [
      {
        source: "/old-blog/:slug",
        destination: "/blog/:slug",
        permanent: true,
      },
    ];
  },
};

module.exports = nextConfig;
```

## Common deployment issues

- *Build Failures with "Module not found"*: Verify `package.json` includes all required dependencies and ensure your lockfile (`package-lock.json` or `yarn.lock`) is committed to your repository.
- *Port Binding Errors*: By default, `next start` binds to `0.0.0.0` and the port defined by Render's `PORT` environment variable, so no extra configuration is needed.

  However, if you are using a *custom server* (e.g., `server.js` with Express/Node http), you must explicitly read `process.env.PORT` and bind to `0.0.0.0`:

```javascript
const port = process.env.PORT || 3000;
// ... server setup ...
server.listen(port, "0.0.0.0", () => {
  console.log(`Server listening on port ${port}`);
});
```

- *Memory Exhaustion During Builds*: If your build fails with OOM errors, increase Node.js heap size using an environment variable:

```yaml
envVars:
  - key: NODE_OPTIONS
    value: --max-old-space-size=4096
```

- *"Invalid src prop" Image Errors*: Add external domains to your `next.config.js` image configuration as shown in the Image Optimization section.
- *504 Gateway Timeouts*: While Render supports long-running requests (up to 100 minutes), 504 errors usually indicate that your application is hanging or crashing before sending a response. Check your logs for unhandled exceptions or infinite loops in `getServerSideProps`. Also ensure your Node.js server (if custom) isn't enforcing its own shorter timeout.

## Monorepo configuration

For monorepos (using tools like Turborepo or Nx), specify the root directory where your Next.js application resides:

```yaml
services:
  - type: web
    name: nextjs-app
    runtime: node
    rootDir: packages/web
    buildCommand: npm install && npm run build
    startCommand: npm start
```

## Production considerations

> <p><strong>Render Advantage: Preview Environments</strong></p>
> <p>Enable <a href="https://render.com/docs/preview-environments">Pull Request Previews</a> to automatically deploy a temporary instance of your Next.js app for every pull request. This allows your team to test API routes, UI changes, and full end-to-end flows in a production-like environment before merging code.</p>

- *Custom Domains and SSL*: Render automatically provisions and renews free TLS certificates for [Custom Domains](https://render.com/docs/custom-domains). All HTTP traffic is automatically redirected to HTTPS.
- *Database Connections*: Implement connection pooling to prevent exhausting [database connections](https://render.com/docs/postgresql-connection-pooling). When using serverless functions or API routes, global connection objects are essential.

  Example using `pg`:

  ```javascript
  import { Pool } from "pg";

  let pool;

  if (!global.pool) {
    global.pool = new Pool({
      connectionString: process.env.DATABASE_URL,
      max: 20, // Set pool max size
    });
  }
  pool = global.pool;

  export default pool;
  ```

- *Middleware Behavior*: Next.js Middleware runs as a standard Node.js process on Render Web Services, not in a specialized "Edge Runtime" environment. This means you have access to the full Node.js API, but you should still keep middleware lightweight to avoid latency on every request.
- *Auto-Scaling*: SSR workloads can be CPU-intensive. Enable Render's [autoscaling](https://render.com/docs/scaling) to automatically add instances during traffic spikes and scale down during quiet periods, ensuring consistent performance without over-provisioning.
- *Monitoring*: Integrate application monitoring and structured logging for production issue diagnosis. Render's log streams can be sent to Datadog, LogDNA, or other providers.

The patterns in this guide form a foundation for adapting to your specific Next.js architecture. Understanding how rendering modes, environment variables, and Next.js features interact with Render's infrastructure enables you to make informed configuration decisions that optimize for your application's requirements.

## Next steps

- Explore the [Next.js Documentation](https://nextjs.org/docs) for deeper dives into App Router, data fetching, and advanced caching strategies.
- Check out [Render's Next.js Guide](https://render.com/docs/deploy-nextjs-app) for platform-specific tutorials and quickstarts.

## FAQ

###### What is the difference between ASGI and WSGI?

ASGI (Asynchronous Server Gateway Interface) supports async I/O, WebSockets, and long-lived HTTP connections, while WSGI (Web Server Gateway Interface) handles one request per worker synchronously. FastAPI requires ASGI servers like Uvicorn or Hypercorn because it's built on Python's asyncio for true concurrency.

###### What ASGI server should I use for FastAPI in production?

The recommended setup is Uvicorn with Gunicorn using the UvicornWorker class. This combination provides multi-process performance and robust lifecycle management. Your start command would look like: `gunicorn main:app -k uvicorn.workers.UvicornWorker`.

###### Can I deploy FastAPI without Docker?

Yes. Platforms like Render provide native Python runtime support that handles dependency installation, virtual environment setup, and ASGI server configuration automatically. You only need to specify build and start commands in most cases.

###### How does FastAPI handle static files in production?

While FastAPI can serve static files in development, production deployments should offload static assets to a CDN or dedicated static site service. This prevents file I/O from blocking async workers and improves scalability.

###### What databases work best with FastAPI?

FastAPI integrates well with both relational databases (PostgreSQL, MySQL) and NoSQL services (MongoDB, Redis). For production, look for platforms offering managed databases with connection pooling, SSL enforcement, and private networking to optimize latency and throughput.

###### How do I manage environment variables for FastAPI deployments?

Production deployments require isolated environment variable management for database credentials, API keys, and feature flags. Most platforms provide encrypted storage with support for staging/production separation. On Render, you can save environment variables and choose when to deploy the changes.

###### Does FastAPI support auto-scaling?

FastAPI itself doesn't handle scaling, but its async architecture maximizes concurrency per instance. Platforms like Render, AWS, and Cloud Run provide horizontal auto-scaling that provisions additional instances when CPU or memory thresholds are breached.

###### What's the easiest platform for deploying FastAPI?

Render offers the simplest deployment experience with automatic Git-based deployments, native Python support, and integrated databases. You connect a GitHub repository, and each commit triggers automatic build and deployment without requiring custom scripts or Docker configuration.

###### How much does it cost to host a FastAPI application?

Costs vary by platform. Render offers a free tier for small services, with paid instances starting at $7/month. Cloud Run uses pay-per-use pricing beneficial for variable traffic. AWS costs depend on the specific services and instance types you choose.

###### Do I need to configure HTTPS for my FastAPI deployment?

On managed platforms like Render, HTTPS is enabled by default with automatic TLS certificate provisioning and renewal via Let's Encrypt. On self-managed infrastructure like AWS EC2, you'll need to configure SSL certificates manually or use a load balancer with certificate management.


