NotAGoodCoder's avatar

Multi-Server Deployments with Envoyer, Github Actions, and Vite

We are deploying our app via Envoyer currently, triggered by Github pushes. We are now to the point that I no longer want to run npm run build on each web server, as we have multiple servers and are running into issues of Vite SOMETIMES generating different hashes on different servers. This of course completely breaks the app, and a re-deploy fixes it.

Instead, I want to run npm run build as part of our CI pipeline, push the assets up to our CDN, and deploy this generated manifest.json with our deployment.

This is actually working perfectly already. The problem I'm running into is that I need to keep old versions of the files so that we can roll back deployments in Envoyer and have the rollback automatically use the correct compiled components.

The problem I have is that we currently deploy to {CDN}/builds/{github.sha1}/assets - This is working 100% perfectly, but the problem is that I have no way to tell Envoyer so update the .env as part of the deployment process so that the URL for ASSET_URL is https://{cdn}/builds/{github.sha1}.assets. I can write a Laravel service provider to do this, but it just feels like a very clunky way to do this and it makes me think that I may be going about this differently than most.

The easy fix would be for me to dump all of the compiled assets into {cdn}/builds/assets, but then I'm going to have a directory in our bucket filled with tens of thousands of old files. I can do that and it will work, but I'm really hoping to figure out a way to separate deployed assets into different directories.

Any thoughts?

0 likes
1 reply
LaryAI's avatar
Level 58

Here's a solution that should give you clean rollbacks while keeping per-deployment assets in distinct directories on your CDN, and let Envoyer deployments use the correct ASSET_URL automatically:

1. Build assets in CI

As you're already doing, run Vite's build process in your GitHub Actions workflow. This produces your manifest and hashed files.

2. Upload assets to CDN using a unique build path

Your approach of {cdn}/builds/{github.sha}/assets is perfect because it keeps each build isolated.

3. Deploy built assets and manifest

Sync the public/build directory and the resulting manifest.json to your CDN at the correct build path during CI.

4. Pass ASSET_URL into the deployment process

Option 1: Use .env file swapping in Envoyer

Envoyer allows you to run custom deployment hooks, including ones that can update the .env file before (or after) the symlink is swapped.

How to do it:

  • In your GitHub Actions workflow, after build, output the build SHA to a temporary file.
  • Upload it with your assets, or output it as an Envoyer environment variable using Envoyer's "Update Environment File" deployment hook.

Example Bash script for Envoyer deployment hook:

php artisan env:set ASSET_URL=https://cdn.example.com/builds/{{ sha }}/assets

Where {{ sha }} could be set as an Envoyer "Deployment Variable", or you echo it as part of the build and pass to the deployment via API or custom scripting. See: Envoyer deployment variables docs.

Option 2: Parameterize ASSET_URL at runtime

For rollbacks, it's crucial the correct value is always available for that release. One clean approach is to use an .env template file in each release with a placeholder (like ASSET_URL=$BUILD_ASSET_URL) and during your CI step, replace it with the actual value:

In GitHub Actions:

echo "ASSET_URL=https://cdn.example.com/builds/${{ github.sha }}/assets" > .env.asset_url

Upload/copy that to your release directory, maybe as part of your Envoyer deploy.

In Envoyer Deployment Hook:

cat .env.asset_url >> .env

Or, if you want to fully replace the value:

sed -i "s|^ASSET_URL=.*$|ASSET_URL=https://cdn.example.com/builds/${DEPLOY_SHA}/assets|" .env

(Where ${DEPLOY_SHA} is passed from GitHub Actions or as an Envoyer variable.)

Option 3: Use release-based asset paths in Laravel config

If you don't want to touch the .env file, you could add logic in a Laravel config file:

// config/app.php

'asset_url' => env('ASSET_URL', 'https://cdn.example.com/builds/' . trim(file_get_contents(base_path('REVISION')), "\n") . '/assets')

And then, ensure your CI/deployer writes the commit sha or build ID to a REVISION file with every release.

5. Cleanup old CDN builds

Finally, address CDN asset bloat by running a scheduled script or GitHub action to prune old {sha}-named asset folders, except for current and N fallbacks.


Summary

  • Stick with {cdn}/builds/{sha}/assets for assets, uploaded via CI.
  • Update each release's .env (or config) with the right per-release ASSET_URL, via CI or an Envoyer hook.
  • When rolling back, Envoyer checks out the previous release, whose .env (or REVISION file) points to the correct asset path.
  • Cleanup CDN as needed.

This matches how tools like Laravel Vapor and other modern platforms handle rollbacks with immutable assets.

Example workflow overview:

name: Deploy

on:
  push:
    branches: [main]

jobs:
  build_upload_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci && npm run build
      - run: aws s3 sync public/build s3://your-bucket/builds/${GITHUB_SHA}/assets
      - run: echo "ASSET_URL=https://cdn.example.com/builds/${GITHUB_SHA}/assets" > .env.asset_url
      # Add a step to call the Envoyer API or set an Envoyer variable with ${GITHUB_SHA}
      # Deploy using Envoyer, which uses .env.asset_url or a template hook to set .env

Let me know if you need example scripts or a more automated approach for Envoyer!

Please or to participate in this conversation.