From c2d1864240bfa75bceb1f510fcd47887a10ad564 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Tue, 2 Dec 2025 14:54:13 -0300 Subject: [PATCH] feat: add export-assets script for CDN asset extraction and update Dockerfiles --- Dockerfile.fips.standalone-infisical | 3 + Dockerfile.standalone-infisical | 3 + backend/package.json | 1 + backend/scripts/export-assets.sh | 77 ++++++++++++++++ docs/docs.json | 1 + docs/self-hosting/guides/cdn-caching.mdx | 106 +++++++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 backend/scripts/export-assets.sh create mode 100644 docs/self-hosting/guides/cdn-caching.mdx diff --git a/Dockerfile.fips.standalone-infisical b/Dockerfile.fips.standalone-infisical index ab1d6fbb7c..9302578fec 100644 --- a/Dockerfile.fips.standalone-infisical +++ b/Dockerfile.fips.standalone-infisical @@ -185,6 +185,9 @@ COPY --from=backend-runner /app /backend COPY --from=frontend-runner /app ./backend/frontend-build +# Make export-assets script executable for CDN asset extraction +RUN chmod +x /backend/scripts/export-assets.sh + ARG INFISICAL_PLATFORM_VERSION ENV INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION diff --git a/Dockerfile.standalone-infisical b/Dockerfile.standalone-infisical index 0674a00e97..faf489b2db 100644 --- a/Dockerfile.standalone-infisical +++ b/Dockerfile.standalone-infisical @@ -174,6 +174,9 @@ ENV CAPTCHA_SITE_KEY=$CAPTCHA_SITE_KEY COPY --from=backend-runner /app /backend COPY --from=frontend-runner /app ./backend/frontend-build +# Make export-assets script executable for CDN asset extraction +RUN chmod +x /backend/scripts/export-assets.sh + ARG INFISICAL_PLATFORM_VERSION ENV INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION diff --git a/backend/package.json b/backend/package.json index 0e17bb2b73..4f9cfdc97a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,7 @@ "outputPath": "binary" }, "scripts": { + "assets:export": "./scripts/export-assets.sh", "binary:build": "npm run binary:clean && npm run build:frontend && npm run build && npm run binary:babel-frontend && npm run binary:babel-backend && npm run binary:rename-imports", "binary:package": "pkg --no-bytecode --public-packages \"*\" --public --target host .", "binary:babel-backend": " babel ./dist -d ./dist", diff --git a/backend/scripts/export-assets.sh b/backend/scripts/export-assets.sh new file mode 100644 index 0000000000..0a1ee37632 --- /dev/null +++ b/backend/scripts/export-assets.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# Export frontend static assets for CDN deployment +# Usage: +# npm run assets:export - Output tar to stdout (pipe to file or aws s3) +# npm run assets:export /path - Extract assets to specified directory +# npm run assets:export -- --help - Show usage + +set -e + +ASSETS_PATH="/backend/frontend-build/assets" + +show_help() { + cat << 'EOF' +Export frontend static assets for CDN deployment. + +USAGE: + docker run --rm infisical/infisical npm run assets:export [-- OPTIONS] [PATH] + +OPTIONS: + --help, -h Show this help message + +ARGUMENTS: + PATH Directory to export assets to. If not provided, outputs + a tar archive to stdout. + +EXAMPLES: + # Export as tar to local file + docker run --rm infisical/infisical npm run assets:export > assets.tar + + # Extract to local directory + docker run --rm -v $(pwd)/cdn-assets:/output infisical/infisical npm run assets:export /output + + # Pipe directly to S3 (using aws cli on host) + docker run --rm infisical/infisical npm run assets:export | \ + tar -xf - -C ./tmp-assets && \ + aws s3 sync ./tmp-assets s3://my-bucket/assets + +EOF + exit 0 +} + +# Check for help flag +case "${1:-}" in + --help|-h) + show_help + ;; +esac + +# Verify assets exist +if [ ! -d "$ASSETS_PATH" ]; then + echo "Error: Assets directory not found at $ASSETS_PATH" >&2 + echo "Make sure the frontend is built and included in the image." >&2 + exit 1 +fi + +ASSET_COUNT=$(find "$ASSETS_PATH" -type f | wc -l | tr -d ' ') + +if [ $# -eq 0 ]; then + # No path provided - output tar to stdout + echo "Exporting $ASSET_COUNT assets as tar archive to stdout..." >&2 + tar -cf - -C "$(dirname "$ASSETS_PATH")" "$(basename "$ASSETS_PATH")" +else + # Path provided - extract to directory + OUTPUT_PATH="$1" + + if [ ! -d "$OUTPUT_PATH" ]; then + echo "Creating output directory: $OUTPUT_PATH" >&2 + mkdir -p "$OUTPUT_PATH" + fi + + echo "Exporting $ASSET_COUNT assets to $OUTPUT_PATH..." >&2 + cp -r "$ASSETS_PATH"/* "$OUTPUT_PATH/" + + echo "✅ Assets exported successfully!" >&2 + echo " Path: $OUTPUT_PATH" >&2 + echo " Files: $ASSET_COUNT assets" >&2 +fi diff --git a/docs/docs.json b/docs/docs.json index be76a10c17..83e44bb053 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -330,6 +330,7 @@ "self-hosting/guides/custom-certificates", "self-hosting/guides/automated-bootstrapping", "self-hosting/guides/production-hardening", + "self-hosting/guides/cdn-caching", "self-hosting/guides/monitoring-telemetry" ] }, diff --git a/docs/self-hosting/guides/cdn-caching.mdx b/docs/self-hosting/guides/cdn-caching.mdx new file mode 100644 index 0000000000..cc4c978878 --- /dev/null +++ b/docs/self-hosting/guides/cdn-caching.mdx @@ -0,0 +1,106 @@ +--- +title: "CDN Caching for Static Assets" +description: "How to set up CDN caching to prevent version skew issues during deployments" +--- + +This guide explains a common issue with frontend asset caching during deployments and how to solve it using a CDN. + +## The Problem: Version Skew + +Modern frontend build tools like Vite generate content-hashed filenames for static assets (e.g., `main-abc123.js`). Each build produces unique filenames based on file contents. During deployments, this can cause a race condition: + +1. User loads `index.html` which references `main-abc123.js` +2. New deployment replaces containers with a new build +3. New containers only serve `main-xyz789.js` (new build) +4. User's browser requests `main-abc123.js` from cached HTML +5. Request returns **404** — the old asset no longer exists + +This results in broken pages, failed SPA navigation, and requires users to manually refresh. + + +This is a documented limitation in Vite's official guidance: [Load Error Handling](https://vite.dev/guide/build#load-error-handling) + + +### Current Behavior + +Infisical includes a built-in workaround that detects version mismatches and triggers a page reload. While functional, this introduces a noticeable delay for users during deployments. + +## The Solution: External Asset Storage + +The solution is to store static assets externally (e.g., S3, GCS, Azure Blob) and serve them through a CDN (e.g., CloudFront, Cloud CDN, Cloudflare). Assets are uploaded **before** container deployment, ensuring old versions remain available. + +### How It Works + +```mermaid +flowchart LR + User[User Browser] + CDN[CDN] + S3[(Object Storage)] + App[Your Infrastructure] + + User --> CDN + CDN -->|"/assets/*"| S3 + CDN -->|"/* (default)"| App +``` + +The key points: + +- **Asset persistence**: Old assets remain available even after new deployments +- **Deployment order**: Upload new assets before deploying new containers +- **Long cache TTL**: Content-hashed files can be cached indefinitely (we recommend 30 days) +- **Automatic cleanup**: Configure lifecycle rules to expire old assets after 30 days + +At Infisical, we use **CloudFront + S3** for this purpose, but you can use any CDN and object storage combination that fits your infrastructure. + +## Exporting Assets + +Infisical provides a built-in command to export frontend assets from the Docker image: + +```bash +# Export as tar archive to stdout +docker run --rm infisical/infisical npm run assets:export > assets.tar + +# Extract the archive +tar -xf assets.tar +ls assets/ # Content-hashed JS/CSS files +``` + +Or export directly to a mounted directory: + +```bash +docker run --rm -v $(pwd)/cdn-assets:/output \ + infisical/infisical npm run assets:export /output +``` + +### What Gets Exported + +The command exports the `/assets` directory containing: + +- JavaScript bundles (e.g., `main-abc123.js`, `chunk-def456.js`) +- CSS files (e.g., `styles-789xyz.css`) +- Other static assets with content hashes + +These files are safe to cache with long TTLs because their filenames change whenever the content changes. + +## Integration with Your Pipeline + +The general deployment flow should be: + +1. **Build** your new Docker image (or pull the official Infisical image) +2. **Export** assets using `npm run assets:export` +3. **Upload** assets to your object storage +4. **Deploy** the new container version + +```bash +# Example: Export and upload to S3 +docker run --rm infisical/infisical:$VERSION npm run assets:export > assets.tar +tar -xf assets.tar +aws s3 sync assets s3://your-bucket/assets --cache-control "public, max-age=2592000" + +# Then deploy your container +``` + + +Always upload assets **before** deploying the new container. This ensures the assets referenced by the new `index.html` exist before users can access them. + +