Passer de Render à votre propre serveur
Suivez ça sur votre propre serveur. Commencer gratuitement, sans carte
Les tarifs de Render grimpent vite dès qu’un service web a besoin de plus que la plus petite instance, et son Postgres géré ajoute sa propre ligne par-dessus. Certaines équipes se butent au comportement de mise en veille du plan gratuit et ne veulent quand même pas payer un plan plus gros juste pour garder un projet secondaire éveillé. D’autres veulent simplement un serveur qu’elles contrôlent, où aucune plateforme ne décide des tailles d’instance disponibles ni de ce qui arrive si une carte au dossier expire. Ce guide déplace un service web Render, son worker en arrière-plan s’il y en a un, et sa base Postgres gérée vers un serveur que vous possédez. À la fin, vous aurez la même app qui tourne depuis son propre Dockerfile sur un serveur que vous avez provisionné, sa base de données restaurée avec les mêmes lignes et le même schéma, HTTPS émis automatiquement, et une tâche de sauvegarde qui garde des copies des données par la suite.
Ce qu’il vous faut
- Un nouveau serveur. On utilise un Hetzner CX23 (2 vCPU, 4 Go de RAM, 40 Go NVMe), affiché à 5,99 € par mois hors TVA. C’est assez de marge pour faire tourner le conteneur de l’app, un processus worker s’il y en a un, Postgres, et un proxy inverse Caddy sur la même machine, ce qui couvre ce dont un service web Render typique plus sa base de données a besoin. Si vous préférez comparer les fournisseurs d’abord, Railyard tient un guide des coûts d’hébergement avec des chiffres à jour sur plusieurs d’entre eux.
- Un nom de domaine que vous pouvez pointer vers le nouveau serveur. Un changement d’enregistrement A prend quelques minutes à se propager, et HTTPS ne sera pas émis avant que ce soit fait.
- Une clé API Render. Créez-en une depuis Account Settings dans le tableau de bord de Render (dashboard.render.com/u/settings), puis « Add API Key ». Render émet des clés qui donnent accès à tout votre compte plutôt qu’une option restreinte en lecture seule, alors gardez-la comme vous garderiez n’importe quel autre identifiant, et supprimez-la une fois la migration terminée.
- Le dépôt de votre app, avec le Dockerfile que Render utilisait déjà pour le build. Si Render construisait votre service à partir d’un runtime sans Docker, vous devrez ajouter un Dockerfile maintenant, puisque rien sur le nouveau serveur ne construit votre app à votre place autrement.
Railyard tient aussi un guide de migration dédié à Render avec une comparaison fonctionnalité par fonctionnalité, si vous voulez voir ce qui change avant de commencer à déplacer quoi que ce soit.
Étape 1 : récupérer votre configuration actuelle depuis l’API de Render
Avant de toucher au nouveau serveur, obtenez un portrait exact de ce que Render fait tourner réellement, plutôt que de vous fier à votre mémoire ou à un vieux README. L’API de Render vous permet de relire tout ce qui configure votre service.
export RENDER_API_KEY="rnd_your_key_here"
curl -s -H "Authorization: Bearer $RENDER_API_KEY" \
"https://api.render.com/v1/services?limit=100" \
| jq '.[].service | {id, name, type, repo}'
Ça liste tous les services de votre compte avec leur id. Prenez celui que vous déplacez et récupérez son détail complet, ses variables d’environnement et ses domaines personnalisés :
SERVICE_ID="srv-xxxxxxxxxxxxxxxxxxxx"
curl -s -H "Authorization: Bearer $RENDER_API_KEY" \
"https://api.render.com/v1/services/$SERVICE_ID" \
| jq '{name, type, repo, branch, startCommand: .serviceDetails.envSpecificDetails.startCommand, ports: .serviceDetails.openPorts}'
curl -s -H "Authorization: Bearer $RENDER_API_KEY" \
"https://api.render.com/v1/services/$SERVICE_ID/env-vars?limit=100" \
| jq '.[].envVar'
curl -s -H "Authorization: Bearer $RENDER_API_KEY" \
"https://api.render.com/v1/services/$SERVICE_ID/custom-domains?limit=100" \
| jq '.[].customDomain.name'
Vous devriez voir l’URL du dépôt de votre service, sa branche, sa commande de démarrage et son port; la liste complète des variables d’environnement, incluant DATABASE_URL si vous utilisez le Postgres géré de Render; et les noms de domaine personnalisés rattachés au service. Notez tout ça, vous aurez besoin de chaque élément dans les étapes suivantes.
Une chose que cette API ne vous donnera jamais : le contenu des fichiers secrets rattachés au service. Render n’a aucun point d’accès pour ça, alors ouvrez chacun d’eux maintenant dans le tableau de bord et copiez leur contenu à la main avant de perdre l’accès au compte.
Étape 2 : provisionner le nouveau serveur
Créez le serveur (un Hetzner CX23 ou équivalent, sous Ubuntu), ajoutez votre clé SSH à la création, et notez son IP publique. Connectez-vous ensuite et mettez les paquets à jour.
ssh root@<server-ip>
apt-get update && apt-get upgrade -y
Vous devriez voir l’invite de commande revenir sans mise à jour en attente.
Étape 3 : installer Docker
curl -fsSL https://get.docker.com | sh
Vérifiez que ça a fonctionné :
docker --version
docker compose version
Les deux commandes devraient afficher un numéro de version. Le script d’installation de Docker fournit Compose comme plugin, il n’y a donc pas d’étape d’installation séparée pour ça.
Étape 4 : apporter votre dépôt et votre Dockerfile sur le serveur
git clone <your-repo-url> /opt/app
cd /opt/app
Si Render construisait déjà ce service à partir d’un Dockerfile, c’est réglé. S’il utilisait un runtime de type buildpack (Ruby, Node, etc.), ajoutez un Dockerfile au dépôt maintenant, puisque ce buildpack n’existe pas en dehors de Render.
Créez un fichier d’environnement à côté du dépôt avec les valeurs récupérées à l’étape 1, plus un mot de passe pour la nouvelle base de données :
# /opt/app/.env
POSTGRES_PASSWORD=choose-a-strong-password-here
Toutes les autres variables que Render vous a listées vont dans ce même fichier, et sont référencées depuis docker-compose.yml dans les étapes suivantes.
Étape 5 : sortir la base de données de Render
Ouvrez la page Info de la base de données dans le tableau de bord de Render et trouvez la chaîne de connexion externe qui y est affichée (elle fonctionne depuis l’extérieur du réseau de Render, contrairement à la chaîne interne que les services sur Render utilisent entre eux). Utilisez-la pour faire un dump :
pg_dump --format=custom --no-owner --no-acl \
"postgresql://user:password@dpg-xxxxxxxx.oregon-postgres.render.com/dbname" \
-f render_dump.pgsql
Utilisez un pg_dump local qui correspond à la version majeure de Postgres que vous ferez tourner sur le nouveau serveur (16, pour correspondre à l’image de l’étape suivante). Vous devriez voir apparaître un fichier render_dump.pgsql, d’une taille à peu près proportionnelle à vos données. Confirmez qu’il est lisible avant de continuer :
pg_restore --list render_dump.pgsql | head
Vous devriez voir la liste de vos tables, séquences et index.
Étape 6 : démarrer Postgres sur le nouveau serveur et restaurer
Écrivez le fichier Compose pour toute la pile dès maintenant, pour que l’étape 6 n’ait besoin que du service de base de données en marche :
# /opt/app/docker-compose.yml
services:
app:
build: .
restart: unless-stopped
environment:
DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/app_production
RAILS_ENV: production
depends_on:
- db
expose:
- "3000"
worker:
build: .
command: bundle exec sidekiq
restart: unless-stopped
environment:
DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/app_production
RAILS_ENV: production
depends_on:
- db
db:
image: postgres:16.4-alpine
restart: unless-stopped
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: app_production
volumes:
- pgdata:/var/lib/postgresql/data
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
depends_on:
- app
volumes:
pgdata:
caddy_data:
Retirez le service worker si Render n’en faisait pas tourner un pour cette app. Démarrez seulement la base de données pour l’instant :
docker compose up -d db
Copiez le dump dedans et restaurez-le :
docker cp render_dump.pgsql $(docker compose ps -q db):/tmp/render_dump.pgsql
docker compose exec db pg_restore --no-owner --no-acl \
-U app -d app_production /tmp/render_dump.pgsql
Vérifiez que ça a bien atterri :
docker compose exec db psql -U app -d app_production -c '\dt'
Vous devriez voir les mêmes noms de tables que ceux vus avec pg_restore --list à l’étape 5.
Étape 7 : démarrer le reste de la pile
docker compose up -d --build
Vérifiez :
docker compose ps
Vous devriez voir app, worker (s’il y en a un), db et caddy tous listés comme en marche.
Étape 8 : pointer le DNS et laisser Caddy émettre HTTPS
Ajoutez un enregistrement A pour votre domaine pointant vers l’IP du serveur. Écrivez le Caddyfile :
# /opt/app/Caddyfile
app.example.com {
reverse_proxy app:3000
}
Remplacez app.example.com par chaque domaine listé à l’étape 1, un bloc par domaine si vous en avez plusieurs. Redémarrez Caddy pour qu’il prenne le fichier en compte :
docker compose restart caddy
Vous devriez voir un certificat valide émis en quelques secondes après que l’enregistrement DNS se résout : curl -I https://app.example.com renvoie un HTTP/2 200 sans avertissement de certificat.
Étape 9 : mettre en place les sauvegardes
Ajoutez une tâche cron quotidienne sur le serveur qui fait un dump de la base de données et garde une fenêtre de copies :
mkdir -p /opt/backups
crontab -e
0 3 * * * docker compose -f /opt/app/docker-compose.yml exec -T db \
pg_dump -U app app_production | gzip > /opt/backups/app_$(date +\%F).sql.gz \
&& find /opt/backups -name '*.sql.gz' -mtime +14 -delete
Lancez la commande à la main une fois pour la confirmer avant d’attendre 3h du matin :
docker compose exec -T db pg_dump -U app app_production | gzip > /opt/backups/app_test.sql.gz
ls -lh /opt/backups
Vous devriez voir un fichier .sql.gz d’une taille non nulle.
Étape 10 : basculer, avec Render encore en marche comme filet de secours
Gardez le service Render et sa base de données intacts pendant toutes les étapes ci-dessus. Avant de déplacer le DNS pour de vrai, confirmez que le nouveau serveur sert correctement l’app avec une requête directe :
curl -H "Host: app.example.com" http://<server-ip>
Une fois que ça, une connexion, et un chemin d’écriture sont tous validés contre la base de données restaurée, déplacez le DNS du domaine vers le nouveau serveur (si ce n’est pas déjà fait à l’étape 8) et laissez-lui le temps de se propager complètement. Ce n’est qu’après avoir confirmé que le trafic arrive au nouveau serveur que vous devriez mettre en pause ou supprimer le service Render.
Étape 11 : garder l’app à jour
cd /opt/app
git pull
docker compose up -d --build
Compose ne reconstruit et ne redémarre que les services dont l’image a réellement changé, donc un petit changement de code n’arrête pas la base de données ou Caddy. Pour une app qui ne tolère même pas quelques secondes de redémarrage, il faudrait plutôt un second conteneur d’app et un basculement par répartiteur de charge, ce qui dépasse ce que couvre cette configuration à un seul serveur.
Le raccourci : le faire avec Railyard
Tout ce qui précède (lire la configuration d’un service Render via son API, récupérer une chaîne de connexion de base de données externe, en faire un dump puis la restaurer) est exactement ce que fait Applications → Import de Railyard pour vous. Pointez-le vers un jeton d’API Render et il lit vos apps, montre un plan par app avant que vous confirmiez quoi que ce soit, et crée l’app sur le serveur de votre choix. Pour un service Render en particulier, ça rapporte le dépôt et la branche, les variables d’environnement, la commande de démarrage et les domaines personnalisés; un worker en arrière-plan devient un processus worker sans URL web, la même distinction que vous feriez à la main à l’étape 6. L’API de Render ne donne toujours à personne le contenu des fichiers secrets, y compris à Railyard, alors c’est le seul morceau que vous ajoutez vous-même après l’import, comme à l’étape 1 ci-dessus. L’import depuis Render est actuellement en bêta, alors lisez l’écran du plan avant de confirmer, de la même façon que vous revérifieriez les valeurs à l’étape 1.
Le volet base de données est aussi offert seul, en dehors d’un import complet d’app. Import data de Railyard accepte soit une URL de base de données source en direct, soit un fichier de dump téléversé, sauvegarde toujours d’abord la base de données de destination pour qu’un import échoué ou annulé restaure automatiquement, et peut lancer les migrations de votre app juste après si vous choisissez « deploy after import ». Ça couvre les dumps jusqu’à 5 Go par défaut.
Tous les détails sont dans Import from another platform et Databases and backups. Le plan gratuit de Railyard couvre un déplacement de cette taille sans carte : un serveur plus un serveur de base de données et trois apps.
Dépannage
Un appel curl contre l’API de Render renvoie 401 ou 403. Les clés API de Render ne sont pas restreintes par portée, il n’y a pas d’option lecture seule ou limitée, alors une requête refusée veut presque toujours dire que la clé a été mal tapée, révoquée, ou régénérée après que vous l’ayez copiée, pas une permission manquante. Générez-en une nouvelle depuis Account Settings et réessayez.
Le worker en arrière-plan ne démarre pas, ou se met à écouter sur un port qu’il ne devrait pas. Un worker en arrière-plan sur Render n’a jamais eu d’URL publique au départ, alors ne donnez pas à son service Compose de ligne expose ni de bloc dans le Caddyfile. S’il plante plutôt, vérifiez que sa commande de démarrage est bien celle du worker (bundle exec sidekiq, un consommateur de file d’attente, etc.), pas celle du service web copiée par erreur.
L’app démarre mais plante aussitôt sur le port où elle écoute. La commande de démarrage récupérée à l’étape 1 était ajustée pour l’environnement de Render, où Render injecte lui-même $PORT. Votre nouveau conteneur ne définira pas cette variable à moins que vous le fassiez, alors définissez PORT explicitement dans le bloc environment de Compose pour qu’il corresponde à ce que vous exposez, ou modifiez la commande de démarrage pour qu’elle se lie à un port fixe.
Caddy n’émet jamais de certificat. Lancez docker compose logs caddy et cherchez des échecs d’autorisation répétés ou des délais dépassés. Ça veut presque toujours dire que l’enregistrement A du domaine ne pointe pas encore vers ce serveur. Comparez dig +short app.example.com à l’IP du serveur lui-même (curl -4 icanhazip.com lancé sur le serveur) et attendez qu’ils correspondent avant de vous attendre à ce que HTTPS fonctionne.
L’app échoue à démarrer en se plaignant d’un fichier manquant. Si le chemin dans l’erreur correspond à l’endroit où Render stockait un fichier secret pour ce service, c’est la seule chose que l’API de Render ne vous donne jamais. Ouvrez le fichier dans le tableau de bord de Render, copiez son contenu, et soit recréez-le comme un fichier que votre conteneur monte ou lit, soit transformez son contenu en variable d’environnement si l’app peut le lire de cette façon à la place.
FAQ
Est-ce que ça cause un temps d’arrêt ? Pas pour la base de données : le Postgres des deux plateformes reste en marche et accessible en lecture jusqu’à l’étape 10, puisque vous lisez depuis la copie de Render et écrivez sur la vôtre. L’app elle-même a une courte fenêtre entre la propagation du changement DNS et le nouveau serveur pleinement réchauffé et servant les mêmes données, typiquement bien en dessous d’une minute si vous avez déjà confirmé la requête directe par IP à l’étape 10.
Qu’en est-il du comportement de mise en veille du plan gratuit de Render ? C’est propre aux services web gratuits de Render, qui s’endorment après une période d’inactivité et prennent quelques secondes à se réveiller à la prochaine requête. Un conteneur sur votre propre serveur tourne en continu une fois démarré, il n’y a pas d’état de veille équivalent à prévoir.
Puis-je garder Render comme filet de secours pendant le déplacement ? Oui, c’est exactement ce que l’étape 10 permet : laissez le service Render et sa base de données tourner sans y toucher jusqu’à ce que vous ayez confirmé que les connexions, les écritures et toute tâche en arrière-plan fonctionnent correctement contre le nouveau serveur, puis déplacez le DNS et ne mettez en pause ou n’annulez Render qu’après.
Dois-je recréer mes domaines personnalisés moi-même ? Vous avez déjà la liste complète depuis l’étape 1. Pointez le DNS de chaque domaine vers le nouveau serveur et ajoutez un bloc correspondant dans le Caddyfile pour chacun; Caddy demande et renouvelle HTTPS pour chaque domaine de la même façon.
Est-ce que l’import de Railyard rapporte les fichiers secrets automatiquement ? Non. C’est la seule chose que ni le chemin manuel ni l’import ne peuvent automatiser, puisque l’API de Render ne donne le contenu des fichiers secrets à rien en dehors de Render, Railyard inclus. Vous les ajoutez une fois, à la main, après que l’import soit terminé, comme vous le feriez à l’étape 1.
Quitter Render ne veut pas dire abandonner le Postgres géré ou le HTTPS automatique. Ça veut dire faire tourner les mêmes morceaux (Docker Compose, Caddy, Postgres) vous-même, sur un serveur dont vous avez fixé le prix avant même de vous y inscrire. Que vous passiez par les étapes ci-dessus à la main ou que vous laissiez Applications → Import s’occuper de copier le dépôt, les variables et la base de données, vous finissez avec une app que vous pouvez voir, dans laquelle vous pouvez vous connecter par SSH, et que vous pouvez sauvegarder sans demander la permission à un tableau de bord d’abord.
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.