Rails Static Assets

Hi All,

Wondering if there’s a way to get the Render web server serving static assets built by Rails - in the docs you suggest enabling Rails’ built in file server, but ideally that would happen beforehand.

Probably possible to do something with Docker, but I wondered if there were magic paths or extensions that render understood :slight_smile:

Nik

Hi @nikz, I’ll be happy to help. Can you say more about your use case? Why is the built-in file server not ideal?

The only downside really is taking up a relatively expensive app server process (in terms of memory footprint etc) to serve an asset when it could be done more efficiently (and potentially faster?) by a dedicated webserver. I don’t think it’s a massive impact unless your application is very read heavy and/or has a lot of static assets.

Hi @nikz ,

You could create a static site (Free Static Site Hosting | Render) to host your static assets by having the build command just ask rails to generate the assets. Your actual rails service could then serve those assets, as long as you configure your service to point to your static site.

1 Like

Hi @dan - thanks, that’s a good idea. I’ll give that a go!

1 Like

@Dan that’s an interesting idea. Can you share more?

This sparked my interest as I’ve been experimenting with Render deployments myself. Here’s what I did…

I created a static site in Render and set the build command to precompile the assets and ./public as the publish directory. I had to add a bunch of environment variables so that the precompile would run successfully.

On the app itself, I set config.asset_host to the URL of the new static site in production.rb

and it seems to be working just fine,

John.

1 Like

Thanks for experimenting, @John_Beynon ! @philipithomas , does @John_Beynon 's example give you enough information to explore further?

Moved it to IAC as well with:

services:
  - type: web
    name: <name>
    env: static
    buildCommand: bundle exec rails assets:precompile
    staticPublishPath: ./public
    envVars:
      - key: RAILS_ENV
        value: production
      - key: SECRET_KEY_BASE
        generateValue: true

and then on the app under envVars

 - key: ASSET_HOST
   fromService:
     name: <NAME as above>
     type: web
     envVarKey: RENDER_EXTERNAL_HOSTNAME
1 Like

Cool - so one thing I’m concerned about is race conditions in the deploys. Specifically - I’ve had issues in the past where:

  1. App finishes deploy
  2. User access App, pointing them to get a new asset (like a .js file)
  3. Asset host doesn’t have the new file yet - so it returns a 404
  4. CDN caches the 404
  5. Asset host finishes deploy - and gets the new asset
  6. CDN continues to return 404 because of the cache

Is this race condition possible in this setup? How do cache-control headers get set on the static assets? Can the 404 cache time be set differently than the static assets?

Our CDN does cache 404 responses, but we purge that cache after every deploy, so new assets will get served correctly.

Ok, thanks - what about the inverse problem?

  1. User loads App (Version 1). It’s a single-page app with a large Javascript bundle that uses code splitting.
  2. App deploys Version 2
  3. User switches pages on single-page app, triggering the loading of a file in the Version 1 bundle (due to code splitting)
  4. Does that V1 file still exist on the CDN?

For a typical CDN - I’d expect a cached file to exist in memory for a period of time after a deploy. (The other solution is to sync new files to an S3 bucket, and never delete the old files).

Yes, we continue to serve assets from V1 to avoid that version mismatch. Should all work for you :slight_smile: V2 assets with the same URL will be replaced, but if the URL of a V1 asset is not overwritten by a V2 asset, you will continue to see the V1 asset.

1 Like

Awesome - thanks!

Now you gotta just release an S3-compatible API for things like user profile photos :grinning_face_with_smiling_eyes:

Good idea! We do have a feature request open for something similar (Cloud Object Storage | Feature Requests | Render), but I’m not sure where it might be on our product roadmap. I encourage you to upvote it and add your usecase, since that’ll help us prioritize and design it :slight_smile:

Ahh, I’ve been battling out of memory issues I think I’ve largely tracked down to having puma serving assets. So this looks like a great solution.

I’m having an issue where my asset fingerprints differ on the static app vs the running rails instance, and I’m having a hard time figuring out what’s going on. Did anyone else run into that? Things I’ve checked:

  • They both have RAILS_ENV set to production
  • They share the rails master key