A Log of Solving Routing and Deployment Issues with Docusaurus i18n
This article is a record of the journey from identifying to resolving two tricky problems I encountered when adding internationalization (i18n) to a site built with Docusaurus v3: "client-side routing collapse" and "container startup failure on Cloud Run."
Prerequisites:
- Site Generator: Docusaurus v3
- Hosting: Google Cloud Run
- Development & Deployment: Docker, GitHub Actions
- Package Manager: pnpm
1. The Problems I Encountered
After configuring i18n, I ran into the following two critical issues back-to-back, both after confirming the production build locally (pnpm build
+ pnpm serve
) and after deploying to Cloud Run.
Problem 1: Client-Side Routing Collapse
When I accessed the site and switched languages using the language switcher, the following symptoms appeared:
- Incorrect URL Accumulation: Every time I switched the language, the locale prefix was repeatedly appended to the URL, like
/en/en/en/...
. - React Hydration Errors: The browser console was flooded with
Warning: Expected server HTML to contain a matching ...
errors. This is a serious error in SPAs, indicating a mismatch between the server-generated HTML and the client's DOM structure. - Ultimately a 404: Attempting to access the malformed URL resulted in the site's 404 page being displayed.
Problem 2: Container Startup Failure on Cloud Run
After applying 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 recorded the following error:
The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable.
This means that the deployed Docker container failed to correctly start the web server on port 8080 as specified by Cloud Run.
2. Identifying the Causes and Solutions
I isolated and identified the cause of each problem step-by-step.
2.1. Solving Problem 1 (Routing Collapse)
-
Cause: The root cause was the lack of SPA (Single Page Application) support in the local preview server. The default settings of the
serve
package I was using at the time could not correctly handle the "pretty URLs" (like/docs/intro/
) generated by Docusaurus. The functionality to internally fall back requests to.../index.html
was not working. As a result, the server was returning a 404 before the client-side React Router could interpret the URL, causing the entire routing system to collapse. -
Solution: I switched the local preview server to
http-server
, which supports SPA mode out of the box.-
Introduce
http-server
# Install as a development dependency
pnpm add -D http-server -
Update the
serve
script inpackage.json
I added the--single
option to make it fall back all requests for unresolvable paths to the rootindex.html
. This ensures that URL resolution is correctly delegated to the client-side React Router."scripts": {
"serve": "http-server ./build --single"
}
-
2.2. Solving Problem 2 (Container Startup Failure)
-
Cause: Ironically,
http-server
, which I introduced to fix Problem 1, was the direct cause of this issue. The fundamental problem was the improper classification of dependencies.Because I had installed
http-server
as adevDependency
(development dependency), it was being removed by thepnpm prune --prod
command during the productionDockerfile
build. Consequently,http-server
did not exist inside the production container, causing the container to exit immediately when it tried to execute the startup commandpnpm run serve
. -
Solution: I moved
http-server
todependencies
(production dependencies) to make it available in the production environment.- Reclassify the dependency
# Remove from devDependencies
pnpm remove -D http-server
# Reinstall as a dependency
pnpm add http-server - Remove the unnecessary package
The old
serve
package was no longer needed, so I removed it from the project completely.pnpm remove serve
- Reclassify the dependency
With these fixes, the container started correctly on port 8080, and the Cloud Run deployment completed successfully.
3. The Improved Development and Deployment Flow
Based on this experience, I standardized the development process as follows.
3.1. Development Commands
- Daily Development (with hot reload):
# Docker environment
docker-compose up
# Local environment
pnpm start - Development in 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 check the production-identical build artifacts using the following command flow.
# 1. Build the static files for production
pnpm build
# 2. Serve the build artifacts with an SPA-compatible server
pnpm serve
3.3. Content Management
I now enforce a practice of placing the translated version of any new page in the appropriate location within the i18n/[locale]/...
directory. Since a page without a translation will result in a 404 error for that language, content synchronization is crucial.
4. Conclusion
This series of troubles was a complex, chain-reaction problem caused by a poor choice of local development server and a dependency management mistake in the Docker build. By systematically isolating the cause at each layer and applying the right tools and configurations, I was able to rebuild a robust development and deployment foundation that allows for stable operation of the i18n feature.