Quitter Heroku Key-Value Store (Redis)
Suivez ça sur votre propre serveur. Commencer gratuitement, sans carte
Si vous avez provisionné Redis sur Heroku à un moment donné ces dernières années, l’extension que vous voyez aujourd’hui s’appelle Heroku Key-Value Store, pas Heroku Redis. Heroku a renommé le produit et l’a fait reposer sur Valkey, un fork compatible Redis, tout en conservant le même plugin CLI heroku-redis et les mêmes noms de variables de configuration. Rien n’a cassé dans votre application, mais c’est un signe de la direction que prend le produit, et c’est le bon moment pour décider si vous préférez héberger Redis vous-même, sur un serveur que vous contrôlez, pour une fraction du prix de l’extension.
Ce guide couvre les deux volets de cette transition : mettre en place votre propre Redis, et transférer réellement les données de l’instance Heroku vers celui-ci sans rien perdre de ce dont votre application dépend.
Ce dont vous avez besoin
- Un serveur. Un Hetzner CX23 (2 vCPU, 4 Go de RAM, 40 Go NVMe, 5,99 € par mois hors TVA) offre largement assez de marge pour exécuter Redis à côté d’une application web petite à moyenne. Si vous avez déjà un serveur qui fait tourner votre application, vous pouvez y ajouter Redis plutôt que d’en provisionner un nouveau.
- Docker et le plugin Docker Compose installés sur ce serveur (ou le paquet
redis-serverde votre distribution, si vous préférez éviter les conteneurs, couvert à l’étape 3). - Un accès SSH au serveur et un utilisateur non root pouvant exécuter
dockerousudo. - Le CLI Heroku et un accès
heroku redisà votre instance actuelle, pour transférer les données.
Vous n’avez pas besoin de nom de domaine pour cela. Redis reste sur le réseau privé du serveur et votre application lui parle directement, jamais via l’internet public.
Étape 1 : déterminez ce pour quoi vous utilisez vraiment Redis
Avant de toucher à un fichier de configuration, vérifiez ce que votre application stocke dans Redis, car cela détermine le degré de prudence nécessaire pour la migration et la façon de configurer la persistance par la suite.
- Cache pur (
cache_storede Rails, le framework de cache de Django, un store de sessions que vous pouvez vous permettre de perdre) : la nouvelle instance peut démarrer vide. Rien à migrer, le cache se remplit à nouveau au fil des requêtes. - Files d’attente de jobs (Sidekiq, Bull, Celery, RQ) : les jobs présents dans Redis au moment du basculement sont du vrai travail pas encore exécuté. Il faut soit vider la file jusqu’à zéro avant de basculer, soit copier les données (étape 4).
- Tout ce que vous seriez contrarié de perdre (des compteurs de limiteur de débit auxquels vous tenez, un classement, un verrou scripté en Lua) : copiez les données.
Sur Heroku, vous pouvez vérifier ce qui pointe réellement vers Redis avec :
heroku config -a your-app-name | grep -i redis
Notez chaque nom de variable que vous voyez. Les extensions Heroku ont historiquement utilisé des noms comme REDIS_URL, REDISCLOUD_URL, REDISTOGO_URL ou OPENREDIS_URL selon le fournisseur d’extension avec lequel vous avez commencé, et le code de votre application lit peut-être spécifiquement l’une de ces variables plutôt qu’un REDIS_URL générique.
Étape 2 : exécutez Redis avec Docker Compose
Ce guide utilise redis:8-alpine, la version majeure stable actuelle de l’image Redis officielle, basée sur Alpine pour un encombrement réduit. Si vous faites déjà tourner d’autres services avec Compose, ajoutez ceci comme un service de plus dans le même fichier ; sinon, cela suffit en soi.
services:
redis:
image: redis:8-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
environment:
REDISCLI_AUTH: ${REDIS_PASSWORD}
volumes:
- redis-data:/data
ports:
- "127.0.0.1:6379:6379"
restart: unless-stopped
volumes:
redis-data:
Quelques choix méritent une explication :
127.0.0.1:6379:6379publie le port uniquement sur l’interface loopback du serveur. Un simple"6379:6379"le publierait sur toutes les interfaces, et la publication de ports de Docker contourne les pare-feux hôtes commeufw, ce qui rendrait Redis joignable depuis l’internet. Si votre application tourne comme un autre service dans le même fichier Compose, vous pouvez carrément retirerportset vous connecter à l’hôte nomméredis.--requirepassdéfinit un mot de passe dès le premier jour. L’image officielle est livrée sans mot de passe configuré. Un mot de passe tient en une ligne et vous protège si un port finit par être exposé par erreur.REDISCLI_AUTHest la variable dontredis-clilit son mot de passe, de sorte que les commandes exécutées dans le conteneur s’authentifient sans mettre le mot de passe sur la ligne de commande.--appendonly yesactive la persistance AOF, de sorte qu’un redémarrage du conteneur n’efface pas vos données. Si vous avez décidé à l’étape 1 que cette instance est un cache pur, vous pouvez carrément retirer ceci ainsi que le volume ; perdre un cache au redémarrage est sans conséquence et vous économisez des E/S disque.--maxmemoryet--maxmemory-policy allkeys-lruplafonnent la mémoire de Redis et lui indiquent d’évincer les clés les moins récemment utilisées une fois ce plafond atteint, plutôt que de refuser les écritures. Dimensionnez cela à une fraction de la RAM de votre serveur, pas sa totalité ; laissez de la place pour votre application et l’OS.
Enregistrez le fichier sous compose.yaml, puis générez un mot de passe dans un fichier .env à côté et démarrez Redis :
echo "REDIS_PASSWORD=$(openssl rand -hex 24)" >> .env
docker compose up -d redis
Vérifiez que le conteneur est actif :
docker compose ps
Vous devriez voir le service redis avec un statut Up. Confirmez ensuite qu’il répond et que le mot de passe fonctionne :
docker compose exec redis redis-cli ping
Vous devriez voir PONG. Si vous voyez NOAUTH Authentication required, la variable REDISCLI_AUTH n’a pas atteint le conteneur ; vérifiez que .env se trouve bien à côté de votre compose.yaml.
Étape 3 : l’alternative sans Docker, une installation par paquet avec systemd
Si vous préférez ne pas utiliser Docker du tout, Redis s’installe proprement comme un paquet système et fournit sa propre unité systemd.
Sur Debian ou Ubuntu :
sudo apt-get update
sudo apt-get install -y redis-server
Modifiez /etc/redis/redis.conf et définissez les mêmes choix que ci-dessus directement dans le fichier de configuration :
requirepass your-generated-password-here
appendonly yes
maxmemory 512mb
maxmemory-policy allkeys-lru
bind 127.0.0.1 ::1
La ligne bind compte ici d’une manière qu’elle ne faisait pas dans la version Compose : sans l’isolation réseau de Docker, Redis écoute directement sur l’hôte, donc liez-le à loopback (ou à votre interface réseau privée) plutôt qu’à 0.0.0.0, sauf si vous avez spécifiquement besoin qu’un autre serveur puisse l’atteindre.
Redémarrez le service pour qu’il prenne en compte les nouveaux réglages, et assurez-vous qu’il démarre au boot :
sudo systemctl enable redis-server
sudo systemctl restart redis-server
sudo systemctl status redis-server
Vous devriez voir active (running) dans la sortie du statut, et redis-cli -a your-generated-password-here ping devrait répondre PONG.
Étape 4 : transférez les données
Si l’étape 1 vous a indiqué que cette instance doit seulement démarrer vide, passez directement à l’étape 5.
Sinon, vous avez besoin d’un moyen de copier les clés. Le centre d’aide d’Heroku indique lui-même qu’il n’offre pas de moyen de télécharger des dumps RDB, donc la copie par snapshot que vous utiliseriez entre deux serveurs que vous contrôlez n’est pas une voie prise en charge par Heroku. Ce qui fonctionne contre n’importe quel serveur Redis ou Valkey, c’est de lire chaque clé avec des commandes ordinaires et de l’écrire sur la nouvelle instance.
D’abord, arrêtez tout ce qui écrit dans Redis (vos dynos web et workers), pour que rien ne change pendant la copie :
heroku ps:scale web=0 worker=0 -a your-app-name
Cela met l’application hors ligne pour la durée de la copie, choisissez donc un moment calme. Utilisez vos propres noms de process ; heroku ps -a your-app-name les liste. Pour une file de jobs, laissez si possible les workers terminer ce qui est en file avant de les réduire, afin qu’il y ait moins à transférer.
Enregistrez ensuite ce script sous copy_redis.py. Il parcourt la source avec SCAN, copie les strings, hashes, lists, sets et sorted sets, conserve la durée de vie restante de chaque clé, et affiche toute clé d’un autre type (les streams, par exemple) pour que vous puissiez la traiter manuellement :
import os
import redis
source_url = os.environ["SOURCE_URL"]
if source_url.startswith("rediss://"):
src = redis.from_url(source_url, ssl_cert_reqs=None)
else:
src = redis.from_url(source_url)
dst = redis.from_url(os.environ["TARGET_URL"])
copied = 0
for key in src.scan_iter(count=500):
kind = src.type(key).decode()
ttl = src.pttl(key)
pipe = dst.pipeline()
pipe.delete(key)
if kind == "string":
pipe.set(key, src.get(key))
elif kind == "hash":
pipe.hset(key, mapping=src.hgetall(key))
elif kind == "list":
pipe.rpush(key, *src.lrange(key, 0, -1))
elif kind == "set":
pipe.sadd(key, *src.smembers(key))
elif kind == "zset":
pipe.zadd(key, dict(src.zrange(key, 0, -1, withscores=True)))
else:
print(f"skipped {key!r} (type {kind})")
continue
if ttl > 0:
pipe.pexpire(key, ttl)
pipe.execute()
copied += 1
print(f"copied {copied} of {src.dbsize()} keys")
ssl_cert_reqs=None est le réglage qu’utilise l’exemple Python d’Heroku lui-même, car Key-Value Store sert le TLS avec des certificats auto-signés. Exécutez le script sur votre nouveau serveur, où le Redis cible écoute sur loopback :
sudo apt-get install -y python3-venv
python3 -m venv venv
venv/bin/pip install redis
SOURCE_URL="$(heroku config:get REDIS_URL -a your-app-name)" \
TARGET_URL="redis://:your-generated-password@127.0.0.1:6379/0" \
venv/bin/python copy_redis.py
Si le CLI Heroku n’est pas installé sur ce serveur, exécutez heroku config:get REDIS_URL -a your-app-name sur votre propre machine et collez la valeur dans SOURCE_URL à la place.
Vous devriez voir à la fin une ligne du type copied 1520 of 1520 keys. Les deux nombres ne diffèrent que par les clés que le script a listées comme ignorées ou les clés qui ont expiré pendant l’exécution. Vérifiez aussi la nouvelle instance de son côté :
docker compose exec redis redis-cli dbsize
Étape 5 : redirigez votre application vers la nouvelle instance
Mettez à jour la variable que votre application lit réellement (celle que vous avez trouvée à l’étape 1) avec la nouvelle chaîne de connexion :
REDIS_URL=redis://:your-generated-password@127.0.0.1:6379/0
C’est le bon hôte quand votre application tourne sur le même serveur en dehors de Compose. Si l’application est un autre service du même compose.yaml, utilisez plutôt redis comme hôte (redis://:your-generated-password@redis:6379/0). Notez le schéma : redis://, pas le rediss:// que vous donnait Heroku, car cette instance ne sert pas le TLS.
Démarrez vos processus web et worker sur le nouveau serveur, surveillez les logs pour voir des connexions Redis réussies, et seulement ensuite retirez l’extension Heroku. Vous pouvez la désigner par son nom d’attachement :
heroku addons:destroy REDIS -a your-app-name
Le CLI vous demande de taper le nom de l’application pour confirmer. Si possible, conservez l’extension un jour ou deux après le basculement comme filet de sécurité (le plan Mini est plafonné à 3 $ par mois) ; cela permet de repérer tout ce qui pointe encore vers l’ancienne URL.
Le raccourci : faites-le avec Railyard
Tout ce qui précède, c’est ce que Railyard met en place quand vous ajoutez Redis depuis l’onglet Resources d’une application. Redis tourne sur votre propre serveur avec un mot de passe, seules les applications de ce serveur peuvent l’atteindre, et chaque application se voit attribuer un numéro de base de données dessus. L’application reçoit REDIS_URL, plus REDIS_HOST, REDIS_PORT, REDIS_PASSWORD et REDIS_DB si votre code lit les parties séparément. Quand une application a besoin de sa propre limite de mémoire et de sa propre politique d’éviction (noeviction pour une file Sidekiq, allkeys-lru pour un cache), Give this app its own Redis sur la carte Redis la déplace vers une instance dédiée et copie ses clés ; Database servers couvre les détails.
Pour transférer des clés depuis Heroku, ouvrez Trusted addresses sur la carte Redis et autorisez votre IP, puis exécutez le script de l’étape 4 depuis votre propre machine avec TARGET_URL pointant vers l’adresse et le mot de passe affichés par la carte.
Ce qui fait gagner le plus de temps si vous venez directement d’Heroku : votre application reçoit la chaîne de connexion sous le nom REDIS_URL, et si votre dépôt lit plutôt l’un des anciens noms d’extension (dans .env.example ou vos fichiers de configuration), Railyard remplit cette variable avec la même chaîne de connexion. Cela couvre REDISCLOUD_URL, REDISTOGO_URL, OPENREDIS_URL, REDISGREEN_URL, REDIS_TLS_URL et les noms se terminant par _REDIS_URI, de sorte que le code écrit pour une ancienne extension continue de fonctionner sans renommage.
Si vous déplacez l’application entière, pas seulement Redis, l’import direct depuis Heroku associe automatiquement une extension heroku-redis ou rediscloud à Redis dans le cadre de l’import, en même temps que vos variables de configuration et votre formation de process. Le guide complet pour tout déplacer se trouve sur Migrer depuis Heroku.
Dépannage
Les écritures commencent à échouer une fois l’instance pleine. Vérifiez maxmemory-policy. La politique Redis par défaut, noeviction, rejette les nouvelles écritures une fois la mémoire pleine plutôt que d’évincer quoi que ce soit, ce qui est correct pour une file dont vous ne pouvez pas vous permettre de perdre des données, mais surprenant si vous avez copié une configuration de cache en vous attendant à ce que les anciennes clés s’effacent simplement avec le temps. Définissez allkeys-lru ou volatile-lru pour des données purement de cache, et conservez noeviction délibérément pour tout ce que vous traitez comme une file ou une source de vérité.
Les données disparaissent après un redémarrage de conteneur ou de serveur. Cela signifie que la persistance AOF ou RDB n’est en fait pas activée, généralement parce que --appendonly yes ou appendonly yes a été oublié, ou que le répertoire de données n’est pas monté sur un vrai volume. Vérifiez que docker compose config montre bien le montage du volume, et confirmez que la persistance est activée avec redis-cli CONFIG GET appendonly.
Votre application n’arrive pas du tout à se connecter. La cause la plus courante après une migration depuis Heroku est un schéma d’URL rediss:// (TLS) resté en place et pointant vers une instance en simple redis://, ou l’inverse. Les extensions Heroku utilisent le TLS ; une instance auto-hébergée ne le fait généralement pas, sauf si vous l’avez placée derrière stunnel ou équivalent. Faites correspondre le schéma de l’URL à ce que le serveur parle réellement.
Mémoire saturée sur un petit serveur. Si maxmemory n’est pas défini, Redis essaiera volontiers d’utiliser toute la RAM disponible, et sur une machine de 4 Go partagée avec votre application et l’OS, cela finit avec l’OOM killer du noyau qui abat le process qui lui paraît le plus gros, pas forcément Redis. Définissez maxmemory explicitement à une valeur bien en dessous de votre RAM totale.
FAQ
Ai-je besoin d’un mot de passe si Redis n’est joignable que depuis mon propre serveur d’application ? Pas strictement, si votre pare-feu bloque vraiment le port 6379 depuis partout ailleurs. Un mot de passe reste quasiment gratuit à mettre en place et vous protège contre une règle de pare-feu que vous auriez oublié d’écrire ou un futur serveur ajouté au même réseau.
Puis-je utiliser la même instance Redis pour le cache et les files de jobs ? Oui, séparées logiquement par numéro de base de données (redis://host:6379/0 pour le cache, /1 pour les files) ou par préfixe de clé, mais soyez délibéré sur la politique d’éviction dans les deux cas ; allkeys-lru évincera sans sourciller un job en file si l’instance se remplit.
Quelle est la différence entre Redis et Valkey, ce que fait maintenant tourner l’extension d’Heroku ? Valkey est un fork de Redis créé après le changement de licence de Redis, maintenu en open source sous la Linux Foundation. Il a forké depuis Redis 7.2 et parle le même protocole, de sorte que les bibliothèques clientes Redis habituelles se connectent à l’un comme à l’autre. C’est pourquoi le code de votre application n’a pas eu besoin de changer quand le moteur de l’extension a changé, et pourquoi le même code fonctionne avec le Redis que vous hébergez vous-même.
Comment en faire une sauvegarde ? Avec AOF activé, les fichiers sous le volume redis-data constituent vos données. Pour une copie que vous pouvez envoyer ailleurs, redis-cli --rdb /data/backup.rdb dans le conteneur écrit un snapshot à un instant donné de votre propre instance ; exécutez-le via cron et copiez le fichier hors du serveur.
Quitter une extension Redis gérée consiste surtout à être honnête avec vous-même à l’étape 1 sur ce qui y est réellement stocké. Une fois que vous le savez, le reste se résume à un fichier Compose, un mot de passe, et un basculement que vous pouvez faire sans que personne ne le remarque.
Pourquoi les gens choisissent Railyard pour ça
- Votre propre compte infonuagique. Déployez sur DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou n'importe quel serveur Linux par SSH, facturé directement par votre fournisseur, sans majoration.
- Un prix fixe par équipe. L'offre gratuite est gratuite pour toujours : un serveur plus un serveur de base de données, trois applications, sans carte. Pro coûte 19 $ par mois pour jusqu'à cinq serveurs et un nombre illimité d'applications. Team coûte 49 $ par mois avec des serveurs illimités. Aucuns frais par application, par déploiement, par siège ou par gigaoctet.
- Des gabarits en un clic. Déployez des applications open source, comme des bases de données, des outils d'analyse et des outils de développement, sur votre propre serveur en un clic.
- Des bases de données gérées. Postgres, MySQL et Redis, avec des sauvegardes planifiées, vérifiées par restauration, et la récupération à un point dans le temps.
- Déploiement par push. Déployez depuis GitHub, GitLab ou la CLI, avec HTTPS automatique et des mises en production sans interruption.