Skip to main content

Troubleshooting Docusaurus i18n: A Case Study on Routing and Deployment Issues

This article chronicles the journey of identifying and resolving two tricky problems encountered after adding internationalization (i18n) to a Docusaurus v3 site: "client-side routing collapse" and "container startup failure on Cloud Run."

Prerequisite Environment
  • Site Generator: Docusaurus v3
  • Hosting: Google Cloud Run
  • Development & Deployment: Docker, GitHub Actions
  • Package Manager: pnpm

Sources

1. The Problems Encountered

After configuring i18n, two critical issues emerged consecutively during local production build verification (pnpm build + pnpm serve) and after deploying to Cloud Run.

Problem 1: Client-Side Routing Collapse

When accessing the site and switching languages using the language switcher, the following symptoms appeared:

  • Incorrect URL Accumulation: Every time the language was switched, the locale prefix was repeatedly appended to the URL, resulting in paths like /en/en/en/....
  • React Hydration Errors: The browser console was flooded with errors like Warning: Expected server HTML to contain a matching .... This is a serious error in SPAs, indicating a mismatch between the server-generated HTML and the client's DOM structure.
  • Eventual 404: The attempt to access the malformed URL ultimately led to the site's 404 page.

Problem 2: Container Startup Failure on Cloud Run

After implementing a fix for Problem 1 and redeploying, a new issue arose: the container itself failed to start on Cloud Run.

  • Container Startup Timeout: The Cloud Run logs showed the following error:

    The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable.

    This meant that the deployed Docker container could not successfully start the web server on the port specified by Cloud Run (8080).

2. Identifying Causes and Solutions

I tackled each problem by isolating and identifying its root cause.

2.1. Resolving Problem 1 (Routing Collapse)

  • Cause: The root cause was the lack of SPA (Single Page Application) support in the local verification server. The default configuration of the serve package I was using at the time could not correctly handle "pretty URLs" like /docs/intro/ generated by Docusaurus. It failed to fall back requests to .../index.html internally. As a result, the server returned a 404 before the client-side React Router could interpret the URL, causing the entire routing system to collapse.

  • Solution: I switched the local verification server to http-server, which supports SPA mode out of the box.

    1. Install http-server

      # Install as a development dependency
      pnpm add -D http-server
    2. Update the serve script in package.json I added the --single option to make all unresolved path requests fall back to the root index.html. This ensures that URL resolution is correctly delegated to the client-side React Router.

      package.json
      "scripts": {
      "serve": "http-server ./build --single"
      }

2.2. Resolving Problem 2 (Container Startup Failure)

  • Cause: Ironically, http-server, which was introduced to fix Problem 1, was the direct cause of this new problem. The fundamental issue was improper classification of dependencies.

    Since http-server was installed as a devDependency (development-only dependency), it was removed by the pnpm prune --prod command executed inside the production Dockerfile. Consequently, http-server did not exist in the final production container, causing the startup command pnpm run serve to fail and the container to exit immediately.

  • Solution: I moved http-server to dependencies (production dependency) to make it available in the production environment.

    1. Reclassify the dependency
      # Remove from devDependencies
      pnpm remove -D http-server
      # Re-install as a dependency
      pnpm add http-server
    2. Remove the unnecessary package The old serve package was no longer needed, so I removed it completely from the project.
      pnpm remove serve

With these fixes, the container started correctly on port 8080, and the Cloud Run deployment completed successfully.

3. Improved Development and Deployment Flow

Based on this experience, I standardized the development process as follows.

3.1. Development Commands

  • Daily Development (Hot Reload):
    # Docker environment
    docker-compose up
    # Local environment
    pnpm start
  • Development for a Specific Language (e.g., English):
    # Docker environment
    docker-compose run --rm --service-ports app pnpm start --locale en --host 0.0.0.0
    # Local environment
    pnpm start --locale en

3.2. Final Check Before Deployment

Before any deployment, I always verify the production build using the following command flow:

# 1. Build the static files for production
pnpm build

# 2. Serve the build artifacts with an SPA-aware server
pnpm serve

3.3. Content Management

When adding a new page, I enforce the practice of also adding its translated version to the appropriate location within the i18n/[locale]/... directory. Pages without a translation will result in a 404 error for that language, so keeping content in sync is crucial.

4. Summary

This series of issues was a compound problem stemming from a poor choice of local development server and a dependency management mistake in the Docker build process. By systematically isolating the cause at each layer and applying the right tools and configurations, I was able to re-establish a robust development and deployment foundation for reliably operating the i18n-enabled site.