Using sorl-thumbnail with Redis on Heroku
I recently added sorl-thumbnail to a project for creating smaller image files. I chose to configure with a Redis Key-Value Store, as I’ve heard Redis is super hip, but ran into trouble during deployment.
In this post, I’ll document how I set up Redis locally with Docker, then in production on Heroku.
Local Docker Setup
The logical place to start is with the sorl-thumbnail documentation.
Install the package
python3 -m pip install sorl-thumbnail
Add package to settings.py
# settings.py INSTALLED_APPS += ['sorl.thumbnail']
Migrate database
python manage.py migrate
This should create a model called KVStore.
Add redis server
If we navigate to the documentation’s requirements page, we’ll see we need some sort of key-value store and image processing library.
As the title suggests, we’re going to use Redis. Let’s add that in our docker-compose.yml file.
# docker-compose.yml version: "3.7" services: web: build: ./django command: python /code/manage.py runserver 0.0.0.0:8000 env_file: - ./.env.dev volumes: - ./django:/code ports: - 8000:8000 - 443:443 depends_on: - db - redis db: image: postgres volumes: - postgres_data:/var/lib/postgresql environment: - POSTGRES_HOST_AUTH_METHOD=trust - POSTGRES_USER=username - POSTGRES_PASS=supercomplexpasswordthatishardtocrack - POSTGRES_DBNAME=pg ports: - "5432:5432" redis: restart: always image: redis:latest ports: - "6379:6379" volumes: - redis_data:/var/lib/redis volumes: postgres_data: redis_data: networks: default:
I’m assuming here that your Django web and db services and volumes are already configured. Use this tutorial to get started with Docker, Django, and Postgres.
First, we’re adding a redis service (redis:) and volume (redis_data). Then, we’re telling the web service that we need redis for things to work properly.
Add a Python image library
If you’ve gotten to this page, you probably already have Pillow installed, but you can use a few different image libraries.
Connect Redis server and client
Lastly, sorl-thumbnail has some settings to configure to link it up to your Redis key-value store.
# settings.py THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.redis_kvstore.KVStore'
But if we try to docker-compose up with just this settings, we receive an error from Django:
**Connection error.** Error 99 connecting to localhost:6379. Cannot assign requested address.
The sorl-thumbnail package tries to use some sensible default values for connecting to the Redis server:
- THUMBNAIL_REDIS_DB = 0
- THUMBNAIL_REDIS_PASSWORD = ''
- THUMBNAIL_REDIS_HOST = 'localhost'
- THUMBNAIL_REDIS_PORT = 6379
The problem is that we called our Redis host redis, so sorl-thumbnail cannot connect.
I like to be explicit about variables at the expense of verbosity. Here’s all my sorl-thumbnail settings:
# settings.py # sorl-thumbnail THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.redis_kvstore.KVStore' THUMBNAIL_REDIS_DB = 0 # the default THUMBNAIL_REDIS_HOST = 'redis' THUMBNAIL_REDIS_PORT = 6379 # the default
Then you should be all set locally!
Heroku Production Setup
Production is a little different, but the principles still apply:
- Redis server
- Redis client
- Connect sorl-thumbnail to server
Sorl-thumbnail uses the redis-py package (the Redis client) to connect to the Redis server. At the time of writing, sorl-thumbnail’s reference documentation only showed our above settings as configurable.
But I went and looked at the code and found a nice little secret! We’ll use it later.
Provision heroku-redis add-on
The Heroku documentation shows you how to create a new heroku-redis add-on from the command line or Heroku dashboard.
heroku addons:create heroku-redis:hobby-dev -a your-app-name
The hobby-dev tier is free.
This will add a REDIS_URL configuration or environment variable to your Heroku app. It should look like “redis://…”.
But we didn’t use this variable to connect our project to Redis in our local environment. How will we use REDIS_URL?
That’s the secret I just mentioned at the end of the previous section!
Configure Django settings to use REDIS_URL
Here’s a link to the sorl-thumbnail code used to connect to Redis. It first searches for a THUMBNAIL_REDIS_URL environment variable, which contains the Redis username, password, host, and port.
We want to use the REDIS_URL variable set by heroku-redis because that URL might change in the future without us knowing, but will always be stored as REDIS_URL.
We need to replace our previous Django settings for sorl-thumbnail and Redis connections.
# sorl-thumbnail THUMBNAIL_KVSTORE = 'sorl.thumbnail.kvstores.redis_kvstore.KVStore' THUMBNAIL_REDIS_URL = os.environ.get('REDIS_URL')
Conclusion
And that should be all! Hopefully everything works for you on your first try and you feel like a genius.
I’ve opened an issue for including the THUMBNAIL_REDIS_URL in the Reference documentation.
Overall lesson today is to keep your eye out for strange anomalies like this. Sometimes there is a way to do what you need to do, but someone hasn’t gotten around to documenting it yet.
Maybe that someone could be you!