I have an application deployed on Heroku that I want to connect to GCS using ActiveStorage. I am explicitly specifying credentials
in config/storage.yml
as specified in the JSON key file and the Rails docs asfollows, where I've replaced my actual project name with my-project
and redacted my client ID:
google:
service: GCS
project: my-project
bucket: my-project-<%= Rails.env %>
credentials:
type: service_account
project_id: my-project
private_key_id: "<%= ENV['GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY_ID'] %>"
private_key: "<%= ENV['GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY'] %>"
client_email: "[email protected]"
client_id: "<redacted>"
auth_uri: "https://accounts.google.com/o/oauth2/auth"
token_uri: "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/my-project-service-account%40my-project.iam.gserviceaccount.com"
universe_domain: "googleapis.com"
Again, the above is derived from a JSON key file I created and download for the service account specified. That service account has Owner permissions.
I have verified from the production Rails console that these settings are appearing as specified under Rails.application.config.active_storage.service_configurations['google']
, and that the ENV vars are filled in correctly in the environment, per the JSON key file.
However, when I try to upload a file in production, I receive the following error and stack trace:
2025-04-09T23:29:11.090818+00:00 app[web.1]: E, [2025-04-09T23:29:11.090754 #11] ERROR -- : [eeabb3d7-6d82-45ea-813a-716c33a5e1df]
2025-04-09T23:29:11.090819+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] OpenSSL::PKey::RSAError (Neither PUB key nor PRIV key):
2025-04-09T23:29:11.090819+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df]
2025-04-09T23:29:11.090820+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] vendor/ruby-3.4.2/lib/ruby/3.4.0/openssl/pkey.rb:356:in 'OpenSSL::PKey::RSA#initialize'
2025-04-09T23:29:11.090820+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] vendor/ruby-3.4.2/lib/ruby/3.4.0/openssl/pkey.rb:356:in 'Class#new'
2025-04-09T23:29:11.090821+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] vendor/ruby-3.4.2/lib/ruby/3.4.0/openssl/pkey.rb:356:in 'OpenSSL::PKey::RSA.new'
2025-04-09T23:29:11.090821+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] googleauth (1.14.0) lib/googleauth/service_account.rb:79:in 'Google::Auth::ServiceAccountCredentials.make_creds'
2025-04-09T23:29:11.090821+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] googleauth (1.14.0) lib/googleauth/default_credentials.rb:50:in 'Google::Auth::DefaultCredentials.make_creds'
2025-04-09T23:29:11.090822+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] googleauth (1.14.0) lib/googleauth/credentials.rb:567:in 'Google::Auth::Credentials#init_client'
2025-04-09T23:29:11.090823+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] googleauth (1.14.0) lib/googleauth/credentials.rb:610:in 'Google::Auth::Credentials#update_from_hash'
2025-04-09T23:29:11.090823+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] googleauth (1.14.0) lib/googleauth/credentials.rb:404:in 'Google::Auth::Credentials#initialize'
2025-04-09T23:29:11.090823+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] google-cloud-storage (1.55.0) lib/google/cloud/storage.rb:114:in 'Class#new'
2025-04-09T23:29:11.090824+00:00 app[web.1]: [eeabb3d7-6d82-45ea-813a-716c33a5e1df] google-cloud-storage (1.55.0) lib/google/cloud/storage.rb:114:in 'Google::Cloud::Storage.new'
I've manually verified that OpenSSL::PKey::RSA.new(ENV['GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY'])
returns successfully in the production console.
I'm out of ideas, other than to bag on GCS and use S3 instead, which is trivial to set up by comparison.
storage.yml
is incorrect. I am unable to find your approach documented?GOOGLE_APPLICATION_CREDENTIALS
being set to point to the key's location or revising your application code to configure the use of the key directly.git
, which both Google and that Rails doc expressly forbid. If I didn't, because the Heroku filesystem is ephemeral (it's recycled every 24 hours), any file I create on the filesystem (e.g. by upload) would be blown away every day, which is not the way to run a prod website. So, I was forced to try the second approach documented on that link to Rails docs.