Application Constraints
Heroku is a cloud hosting environment, and as such is subject to some constraints that you may not be used to in a traditional hosting environment or your local development setup. These constraints exist to ensure that your app can take advantage of Heroku’s scalability and redundancy features.
Read-only Filesystem
Your app is compiled into a slug for fast distribution across the dyno grid. The filesystem for the slug is read-only. This means you cannot dynamically write to the filesystem for semi-permanent storage. The following types of behaviors are not supported:
- Caching pages in the public directory
- Saving uploaded assets to local disk (e.g. with attachment_fu or paperclip)
- Writing full-text indexes with Ferret
- Writing to a filesystem database like SQLite or GDBM
- Accessing a git repo for an app like git-wiki
There are two directories that are writeable: ./tmp and ./log (under your application root). If you wish to drop a file temporarily for the duration of the request, you can write to a filename like #{RAILS_ROOT}/tmp/myfile_#{Process.pid}. There is no guarantee that this file will be there on subsequent requests (although it might be), so this should not be used for any kind of permanent storage.
So how do you accomplish each of the following in a way that is compatible with a cloud environment?
- Caching – Use HTTP headers instead of writing to the filesystem. See an example of dynamically generated image caching.
- Saving uploaded assets – Use an asset store system like Amazon S3. See large static assets.
- SQLite or GDBM – Use the provided PostgreSQL database instead of a filesystem database.
- Accessing a git repo – Sorry, but git-wiki and apps that use a similar technique won’t work on Heroku. As clever as git-wiki is, it is fundamentally non-scalable and non-replicatable.
Further reading: Adam Wiggins’s Read Only Source Trees
SSH access
Since your app is spread across many servers, there is no single place to ssh. In short, forget servers.
You can, if you wish, run shell commands from your app’s console with backticks:
$ heroku console Ruby console for myapp.heroku.com >> `pwd` => "/mnt/home/slugs/1938_94afba3_0815/mnt\n"
However, there is little to be gained from doing so. You can’t write to the filesystem, and each command may run on a different virtual machine. When you find yourself wanting ssh access, there’s probably a better approach that properly accounts for Heroku’s distributed server environment. The heroku command line tool supports most operations you would perform with ssh in traditional hosting environments.
Interactive Rake tasks
Some open source apps, such as Radiant CMS, prompt for information on the console during rake setup tasks. Since the rake command is being run over an HTTP request rather than a tty, it is not possible to answer these prompts.
There are several possible workarounds. You may be able to pass environment variables to the rake command to avoid prompts. Another is that you can do all your database setup locally, then import the resulting configured database to Heroku. Another would be to manually set up your database, using the Heroku console. Finally, you could try editing the rake tasks, removing the prompts and instead filling in your responses in the code.
Generally speaking, interactive rake tasks are bad practice and should be avoided.
Git submodules
Git submodules are not currently supported. We’re evaluating whether or not to support them in the future; in the meantime you’ll need to track any submodules in the main project. You can do so like this:
$ cd myapp $ rm -rf `find . -mindepth 2 -name .git` $ git rm --cache `git submodule | cut -f2 -d' '` $ git rm .gitmodules $ git add . $ git config -l | grep '^submodule' | cut -d'=' -f1 | xargs -n1 git config --unset-all $ git commit -m "brought submodules into the main repo"
Run this command if you’re not sure whether your project uses submodules:
$ find . -mindepth 2 -name .git
If it prints any output, you have submodules.
Large static assets
Heroku is not intended to be used to serve large static assets such as videos, PDFs, or mp3s. The good news is that there are many excellent services that work extremely well for this, so you can integrate your app with one of those. Perhaps the best-known is Amazon S3, and it is the one we recommend.
To use S3 with your Heroku app, upload your assets to your S3 account using the command line, or a graphical browser like this Firefox add-on or this OS X client. Once uploaded, you can make your assets public. Take the URL, which will be something like “http://s3.amazonaws.com/bucketname/filename”, and paste it into your views inside your app wherever you need to serve the asset to your users.
For writing uploaded files to S3, use the S3 gem from within your Ruby code, or use Paperclip or attachment_fu, specifying the S3 storage backend.
Slug size
The size of your compiled slug greatly affects the speed with which is can be distributed across our dyno grid. Smaller slugs will result in apps that are faster to scale and more resilient to partial infrastructure outages.
Your slug size is shown at compile time, or you can approximate it locally by creating a zip or .tar.gz archive of your app without the .git directory, and then adding the size of any gems specified in your .gems manifest.
The maximum slug size is 100MB. Most apps should be far below this size. Anything under 10MB is good. If you exceed 50MB, you should think about trying to lean down your app.
If you find yourself with a large (>= 50MB) slug, here are some techniques for reducing its size:
- Move large assets like PDFs or audio files to asset storage, and described in the previous section.
- Remove files stored inside the app’s repository but which are unnecessary to run the app. For example, PSD mockups, or large design documents.
- Look for ways to reduce the number of gems or plugins required in production. For example, using the rspec-rails gem instead of installing the RSpec plugin in your vendor/plugins.