I'm on a project where the file upload setup uses Active Storage with Amazon S3. But there's no configuration for me to download files from S3 and use them in development.
When I looked at config/storage.yml I saw the following:
local:
service: Disk
root: <%= Rails.root.join("storage") %>
I figured it would be simple, just bring everything inside the S3 Bucket into the app's app/storage folder.
I downloaded the AWS CLI and set it up with my credentials.
Then I synced the S3 files into my app/storage folder:
$ aws s3 sync s3://bucket-name ~/Projects/selected-project/storage
Started the app and it didn't work 🙄.
In ActiveStorage, when the configured service is Disk, after the blob generates the key, the file is saved inside two folders and then the blob key.
I checked the file path in the console and it returned:
> user = User.first
> ActiveStorage::Blob.service.send(:path_for, u.avatar.key)
=> "storage/jt/Y7/jtY7656jGPvfPMUUA8kX6Vb4"
I noticed those folders were the first 4 characters of the key.
I thought of a few solutions:
Write a script to generate those subfolders and place the files in them.
I didn't go with this. It might not work, and since I don't have experience with scripts that change the filesystem, I figured it would take too long.Check how Active Storage generates
path_forand tell it to generate directly without those subfolders.
I searched and didn't find anywhere I could do that.Duplicate the bucket inside S3 and use it for development.
Didn't like the idea, I'd have to pay for storing 2 buckets.Use MinIO.
This was the option I figured was fastest and had the best cost-benefit.
For those who don't know, MinIO is an Open Source project written in Go, designed from the start to be the standard for object storage in private cloud. It's a cloud-native object server with concurrent, scalable and lightweight performance (more info).
I need to set up MinIO like S3, since the app needs to think it's talking to S3. I downloaded MinIO from their site and set up the environment variables:
export MINIO_ACCESS_KEY="minio_storage_development"
export MINIO_SECRET_KEY="minio_storage_development"
export MINIO_REGION_NAME="us-east-1"
It's configured through environment variables. You can check all the settings in the docs. (MinIO docs)
Now just start it:
$ minio server ~/minio_storage
Endpoint: http://xxx.xxx.xxx.xxx:9000 http://xxx.xxx.xxx.xxx:9000 http://127.0.0.1:9000
AccessKey: minio_storage_development
SecretKey: minio_storage_development
Region: us-east-1
Browser Access:
http://xxx.xxx.xxx.xxx:9000 http://xxx.xxx.xxx.xxx:9000 http://127.0.0.1:9000
Command-line Access: https://docs.min.io/docs/minio-client-quickstart-guide
$ mc config host add myminio http://100.100.101.162:9000 minio_storage_development minio_storage_development
Note: I hid my IP with xxx.xxx.xxx.xxx because it's static and I don't want it exposed to the web.
There you go, a MinIO server running on your machine.
I accessed it through my browser at http://127.0.0.1:9000/ using the Access Key and Secret Key, and created a Bucket called rails_app_bucket.
In the Rails settings at config/storage.yml:
local:
service: S3
endpoint: http://127.0.0.1:9000
access_key_id: minio_storage_development
secret_access_key: minio_storage_development
region: us-east-1
bucket: rails_app_bucket
force_path_style: true
I moved all the files I had downloaded from the S3 bucket into the MinIO bucket folder:
$ mv ~/Projects/selected-project/storage/* ~/minio_storage/rails_app_bucket
To sync files from S3 I now use:
$ aws s3 sync s3://bucket-name ~/minio_storage/rails_app_bucket
I also added MinIO to my Procfile.development so the server starts whenever I run the app through foreman:
# Procfile.development
server: bin/rails server
webpacker: bin/webpack-dev-server
redis: redis-server
sidekiq: bundle exec sidekiq
minio: minio server ~/minio_storage
Conclusion
It became really simple to sync the files and not get any 404 errors when running the app, or that Rails error saying the file doesn't exist.
Originally posted at guilherme44.com.

Top comments (0)