<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Audrey Maldonado</title>
    <description>The latest articles on Forem by Audrey Maldonado (@adorism).</description>
    <link>https://forem.com/adorism</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F660153%2F76bb97ba-4479-46af-ab71-a129c59c09d1.jpg</url>
      <title>Forem: Audrey Maldonado</title>
      <link>https://forem.com/adorism</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/adorism"/>
    <language>en</language>
    <item>
      <title>Migration Guide: Heroku to Render</title>
      <dc:creator>Audrey Maldonado</dc:creator>
      <pubDate>Thu, 25 Aug 2022 20:43:00 +0000</pubDate>
      <link>https://forem.com/render/migration-guide-heroku-to-render-53p6</link>
      <guid>https://forem.com/render/migration-guide-heroku-to-render-53p6</guid>
      <description>&lt;p&gt;&lt;a href="https://render.com/docs/migrate-from-heroku" rel="noopener noreferrer"&gt;This guide&lt;/a&gt; will help you migrate a Heroku app, Heroku Postgres database, and Heroku Redis instance to Render. Using Render to run your web services and databases is very similar to running them on Heroku. Additionally, many users have found that Render saves them money and provides additional functionality not available from Heroku. Read more about the &lt;a href="https://dev.to/render-vs-heroku-comparison"&gt;differences between Render and Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This guide and associated Heroku CLI plugin covers most migration use cases. It supports migrating a Heroku app and its Heroku Postgres and Heroku Redis add-ons. You may continue using other Heroku add-ons while your app is running on Render by copying over the appropriate environment variables -- e.g., if you are using Heroku's Sengrid add-on, set &lt;code&gt;SENDGRID_USERNAME&lt;/code&gt; and &lt;code&gt;SENDGRID_PASSWORD&lt;/code&gt; in the Render Dashboard. Please contact us at &lt;a href="mailto:support@render.com"&gt;support@render.com&lt;/a&gt; if you experience any problems migrating from Heroku to Render.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Heroku app is using one of Heroku's &lt;a href="https://devcenter.heroku.com/articles/buildpacks#officially-supported-buildpacks" rel="noopener noreferrer"&gt;official buildpacks&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;Heroku app is not using multiple buildpacks&lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Generate a &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt; using Render's Heroku CLI Plugin&lt;/li&gt;
&lt;li&gt;Create Resources on Render&lt;/li&gt;
&lt;li&gt;Configure Environment Variables&lt;/li&gt;
&lt;li&gt;Copy Data From PostgreSQL&lt;/li&gt;
&lt;li&gt;Update DNS Configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Concept Mapping
&lt;/h2&gt;

&lt;p&gt;Before you start the migration, review the following table to understand how some Heroku concepts map to Render concepts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Heroku&lt;/th&gt;
&lt;th&gt;Render&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web Process (within a Heroku app)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/docs/web-services"&gt;Web Service&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker Process (within a Heroku app)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/docs/background-workers"&gt;Background Worker&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dyno&lt;/td&gt;
&lt;td&gt;An instance of your service on Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heroku Postgres&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/docs/databases"&gt;Render PostgreSQL&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heroku Redis&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/docs/redis"&gt;Render Redis&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heroku Scheduler&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/docs/cronjobs"&gt;Cron Job&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Config Vars&lt;/td&gt;
&lt;td&gt;Environment Variables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 1: Generate a &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install Render's Heroku CLI Plugin
&lt;/h3&gt;

&lt;p&gt;Render has created a plugin for the Heroku CLI to reduce some of the manual migration steps. Install the plugin with the following command.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
The CLI plugin will &lt;strong&gt;not&lt;/strong&gt; modify your Heroku app. It only performs read operations.&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heroku plugins:install @renderinc/heroku-import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run CLI Plugin
&lt;/h3&gt;

&lt;p&gt;The CLI plugin will read information about your app and its add-ons and create two files: &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt;. Here is what the process looks like. Run it from the root of the repository containing your app's code.&lt;/p&gt;

&lt;p&gt;You will then be prompted to select the Heroku Postgres and Heroku Redis add-ons you would like to migrate to Render. After making your selections, a &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt; will be created for you, and instructions will be shown.&lt;/p&gt;

&lt;h4&gt;
  
  
  Excluded Environment Variables
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DATABASE_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HEROKU_&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PASSWORD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REDIS_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REDIS_TLS_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SECRET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Add &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt; to Source Control
&lt;/h3&gt;

&lt;p&gt;The CLI plugin has now created two files: &lt;code&gt;Dockerfile.render&lt;/code&gt; and &lt;code&gt;render.yaml&lt;/code&gt;. The &lt;code&gt;Dockerfile.render&lt;/code&gt; defines how to build your app on Render using a Heroku buildpack. The &lt;code&gt;render.yaml&lt;/code&gt; is Render's &lt;a href="https://dev.to/docs/infrastructure-as-code"&gt;Infrastructure-as-Code&lt;/a&gt; file. It can be used to define multiple services and databases running on Render and their relationship to each other.&lt;/p&gt;

&lt;p&gt;Now, add these two files to source control with the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add Dockerfile.render render.yaml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'Prepare app for deploy to Render'&lt;/span&gt;
git push origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Create Resources on Render
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://dashboard.render.com" rel="noopener noreferrer"&gt;Render Dashboard&lt;/a&gt;, select &lt;strong&gt;New +&lt;/strong&gt; and then &lt;strong&gt;Blueprint&lt;/strong&gt;. Connect your &lt;a href="https://dev.to/docs/github"&gt;GitHub&lt;/a&gt; or &lt;a href="https://dev.to/docs/gitlab"&gt;GitLab&lt;/a&gt; account to Render if you haven't already, and then search for and select your repository.&lt;/p&gt;

&lt;p&gt;Render will create a plan to build and deploy your service along with a Render PostgreSQL database and Render Redis service if your Heroku app was using these and you selected them for import in the Run CLI Plugin step above.&lt;/p&gt;

&lt;p&gt;Review the plan and click &lt;strong&gt;Apply&lt;/strong&gt;, and Render will create your resources.&lt;/p&gt;

&lt;p&gt;If all resources were created successfully, continue to the next step. If there was an error you cannot resolve, please contact us at &lt;a href="mailto:support@render.com"&gt;support@render.com&lt;/a&gt;. We're here to help!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure Environment Variables
&lt;/h2&gt;

&lt;p&gt;You may need to manually create environment variables that contain secrets like passwords or API tokens using the Render Dashboard. The CLI plugin excluded environment variables with names containing values indicating they may contain secrets.&lt;/p&gt;

&lt;p&gt;To create or update environment variables using the Render Dashboard,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Dashboard&lt;/strong&gt; on the left&lt;/li&gt;
&lt;li&gt;Select the service you just created from the list&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Environment&lt;/strong&gt; tab and create or update environment variables (check the CLI plugin output if you'd like to copy from your Heroku Config Vars)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4: Copy Data from PostgreSQL
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PostgreSQL
&lt;/h3&gt;

&lt;p&gt;If you have created a Render PostgreSQL database, you may want to copy the data from your Heroku Postgres database to Render.&lt;/p&gt;

&lt;p&gt;Put your Heroku app into maintenance mode so that no new data is written to the database during the copy. &lt;code&gt;&amp;lt;HEROKU APP NAME&amp;gt;&lt;/code&gt; is the Heroku app that owns the Heroku Postgres add-on.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
This data migration process requires some downtime.&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heroku maintenance:on &lt;span class="nt"&gt;--app&lt;/span&gt; &amp;lt;HEROKU APP NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a backup of the data in you Heroku Postgres database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heroku pg:backups:capture &lt;span class="nt"&gt;--app&lt;/span&gt; &amp;lt;HEROKU APP NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the backup. This will download a file named &lt;code&gt;latest.dump&lt;/code&gt; to your local computer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heroku pg:backups:download &lt;span class="nt"&gt;--app&lt;/span&gt; &amp;lt;HEROKU APP NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import &lt;code&gt;latest.dump&lt;/code&gt; into your Render PostgreSQL database. The value for &lt;code&gt;&amp;lt;EXTERNAL CONNECTION STRING&amp;gt;&lt;/code&gt; can be found on the Render Dashboard page for your database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_restore &lt;span class="nt"&gt;--verbose&lt;/span&gt;  &lt;span class="nt"&gt;--no-acl&lt;/span&gt; &lt;span class="nt"&gt;--no-owner&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;EXTERNAL CONNECTION STRING&amp;gt; latest.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;If your database is larger than 20GB or under heavy load, use &lt;a href="https://help.heroku.com/7U1BTYHB/how-can-i-take-a-logical-backup-of-large-heroku-postgres-databases" rel="noopener noreferrer"&gt;Heroku's instructions&lt;/a&gt; to create a backup of your data. After that has completed, you can use the same &lt;code&gt;pg_restore&lt;/code&gt; command above to import the data to your Render PostgreSQL database.&lt;/p&gt;

&lt;p&gt;Consider using the &lt;code&gt;--jobs&lt;/code&gt; flag available to both the &lt;code&gt;pg_dump&lt;/code&gt; and &lt;code&gt;pg_restore&lt;/code&gt; commands to reduce the time required for backup and restore.&lt;/p&gt;



&lt;h2&gt;
  
  
  Step 5: Update DNS Configuration
&lt;/h2&gt;

&lt;p&gt;If your Heroku app is using a custom domain, follow the instructions to &lt;a href="https://dev.to/docs/custom-domains#configuring-dns-to-point-to-render"&gt;update your DNS configuration to point to Render&lt;/a&gt; instead of Heroku. Note that some downtime may be required between when your DNS changes propogate and when Render provisions a TLS certificate for your domain.&lt;/p&gt;






&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If you would like to migrate a Heroku app to Render that is not using an official Heroku buildpack please contact us at &lt;a href="mailto:support@render.com"&gt;support@render.com&lt;/a&gt; for assistance. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;We are working on supporting migration of Heroku apps that use multiple buildpacks. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>heroku</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>...It Works Fine Locally</title>
      <dc:creator>Audrey Maldonado</dc:creator>
      <pubDate>Fri, 19 Aug 2022 17:40:15 +0000</pubDate>
      <link>https://forem.com/render/it-works-fine-locally-1jam</link>
      <guid>https://forem.com/render/it-works-fine-locally-1jam</guid>
      <description>&lt;p&gt;Our latest blog post, &lt;em&gt;by Support Engineer Alan Pinnell-Smith&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;When you're ready to deploy to production, it can be frustrating to find that your project &lt;em&gt;that worked perfectly on your machine&lt;/em&gt; is now throwing errors in the cloud. As you can imagine, the Support Engineering team at Render helps customers fix these issues on a regular basis, so we’ve created a checklist to help prevent them in the first place and save us all some time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing Code
&lt;/h2&gt;

&lt;p&gt;Deploying a project can feel tough, especially when you're starting out.&lt;/p&gt;

&lt;p&gt;Web development used to be so straightforward. Open up a text editor, write some HTML, add a few images, FTP it all up to a server and you have a website live on the Internet! Simpler times — boring, non-dynamic, table-layout, under-construction-GIF, “best viewed on Netscape Navigator”, hit-counter times, but certainly a lot simpler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kb36zm1wo7io1pbe8tr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kb36zm1wo7io1pbe8tr.png" alt="Render re-imagined as a website from the 1990s" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fast-forward to today, and the number of languages, frameworks, tools, design patterns and a multitude of other aspects surrounding web development are staggering and overwhelming. I've been tinkering on the web since the mid-90s (as you can probably tell from the references above) but the sheer number of choices available to the modern-day web developer makes me wonder how anyone can ever know where to start. In addition to the flood of choices, the pace at which these things evolve can be very hard to keep up with (I'm looking at you, JavaScript!).&lt;/p&gt;

&lt;p&gt;Luckily, there are also a huge number of helpful resources out there: guides, video courses, the obligatory blog/to-do list tutorials, and our very own &lt;a href="https://dev.to/docs"&gt;Render Quickstarts&lt;/a&gt; and &lt;a href="https://github.com/render-examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;. There's also the ever-helpful Stack Overflow when you hit that brick wall of an unexpected error message. Tools like project scaffolding, ORMs, and development servers all make it easier to get started with a new project on your machine. But when it comes to deploying your code, there isn’t enough &lt;a href="https://dev.to/blog/why-render"&gt;help online&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying Code
&lt;/h2&gt;

&lt;p&gt;When I made the change in my career from being a web developer (mainly Ruby) to a Support Engineer a few years ago, one phrase quickly became apparent as a common sign-off: "… it works fine locally". Along with its bedfellows "works for me" and "works on my machine", these are phrases we hear often in development, and especially in developer support.&lt;/p&gt;

&lt;p&gt;Don't get me wrong: it's certainly good to know that there are no glaring code issues preventing a project from spinning up at all. However, the considerations required to deploy a project in the cloud are an essential part of being a web developer these days. Even if it works fine locally, chances are it’s not going to work fine when you put your shiny new project on someone else's machine or platform.&lt;/p&gt;

&lt;p&gt;We're all keen to get our work out there on the web as soon as possible, but environments will differ — sometimes by a lot. You may develop on Windows or macOS or something more exotic, but almost always, you will deploy to some flavor of Linux. Every language, framework and tool will have a particular version and likely a sprawling list of dependencies (each with its own version, dependency and compatibility requirements) goes all the way down. Again, we're lucky to have some great package management tools to keep all that in check for us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/docs/native-environments"&gt;Render Native Environments&lt;/a&gt; help the most commonly used project configurations get up and running as quickly as possible, and we inspect your code to supply some defaults along the way. But because every project is different, the required configuration can be too.&lt;/p&gt;

&lt;p&gt;Here's a checklist that, while focused on Render, will help you regardless of where you're deploying your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Deployment Checklist
&lt;/h2&gt;

&lt;p&gt;This list won't apply to every case, but being mindful of these points may make deploying your application, and debugging any issues with that deployment, a little more tractable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof5nehjz5r560ybs8ij8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof5nehjz5r560ybs8ij8.png" alt="A checklist to avoid common deployment errors" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Versioning&lt;/strong&gt;: Explicitly define versions. Language, package manager and packages. Render has &lt;a href="https://dev.to/docs/native-environments#support-languages-and-runtimes"&gt;default language versions&lt;/a&gt; for native environments, but we'd still recommend you set your own version, e.g. &lt;a href="https://dev.to/docs/node-version"&gt;Node&lt;/a&gt;, &lt;a href="https://dev.to/docs/python-version"&gt;Python&lt;/a&gt;, &lt;a href="https://dev.to/docs/ruby-version"&gt;Ruby&lt;/a&gt;, etc. This way, you can be sure it's the same as the one you developed on and will continue to stay the same if the default changes in future.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt;: Keep configuration in the environment. That may sound familiar if you've ever looked at &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;The Twelve-Factor App&lt;/a&gt;. External service URLs, API keys/tokens, SMTP config, etc. will be easier to manage per environment if stored in &lt;a href="https://dev.to/docs/configure-environment-variables"&gt;environment variables or as secret files&lt;/a&gt;. And if you need to share these across services, check out Render’s &lt;a href="https://dev.to/docs/configure-environment-variables#2-environment-groups"&gt;Environment Groups&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a Health Check&lt;/strong&gt;: Create an HTTP endpoint in your app to check if your application is live. Web Services on Render allow you to specify a &lt;a href="https://dev.to/docs/deploys#health-checks"&gt;health check path&lt;/a&gt; that's used to check that a new deploy is working as expected before transitioning requests to it. It is also used to check if your app is still up after a deploy; Render automatically restarts your app if the health check endpoint is unresponsive. &lt;br&gt; &lt;br&gt; A health check endpoint should touch critical parts of your app, for example, include a simple database connection in it or ping an API endpoint. If your checks pass, return a response code between 200-399 and the deploy will be put live. If the health check responds with a code 400+ the deployment will be marked as a failure and will not proceed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production Builds&lt;/strong&gt;: Verify the dependencies being installed when you build your app in the cloud. Use the same tools you did for development, e.g. npm/Yarn (Node), pip/Poetry (Python), Bundler (Ruby), etc. These tools can also have dependency groups, like devDependencies/dependencies in Node's package.json; make sure your packages are in the right group and be aware that &lt;a href="https://docs.npmjs.com/cli/v8/commands/npm-install#:~:text=With%20the%20%2D%2Dproduction,production%3Dfalse." rel="noopener noreferrer"&gt;production mode may not install development group packages&lt;/a&gt;. &lt;br&gt; &lt;br&gt; Many frameworks also have dev tooling that does things like hot code reloading or just-in-time compilation. The Rails asset pipeline or a Webpack dev server are good examples of this. These development helpers are usually unsuitable for production, so you should build/precompile/transpile as your particular project requires. &lt;br&gt; &lt;br&gt; To keep your Render build command(s) tracked along with your code, it can be handy to put it in a shell script and use that to set up the environment.  A simple Node based example would be:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sh&lt;/span&gt;&lt;span class="cp"&gt;

#!/usr/bin/env bash
&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;exit&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="nx"&gt;errexit&lt;/span&gt;

&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt;
&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Booting your application&lt;/strong&gt;: Make sure you start your app in production mode and are not using a development server. If you're having trouble with a deploy it can sometimes help to try starting your app in production mode on your dev machine to see if it still works fine locally. &lt;br&gt; &lt;br&gt; For &lt;a href="https://dev.to/docs/web-services"&gt;Web Services&lt;/a&gt; in particular ensure the app server has its host &amp;amp; port set to 0.0.0.0 and the PORT environment variable, respectively. You may also want to consider concurrency settings appropriate for your chosen service plan, e.g. worker counts in &lt;a href="https://github.com/hunterloftis/throng#specify-the-number-of-workers" rel="noopener noreferrer"&gt;Throng&lt;/a&gt; (Node), &lt;a href="https://docs.gunicorn.org/en/stable/settings.html#worker-processes" rel="noopener noreferrer"&gt;Gunicorn&lt;/a&gt; (Python), &lt;a href="https://github.com/puma/puma#clustered-mode" rel="noopener noreferrer"&gt;Puma&lt;/a&gt; (Ruby), etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Storage&lt;/strong&gt;: Service runtime instance filesystems are not persistent.  Render service filesystems (and most platforms) are ephemeral, meaning files written after they’re started do not persist after a restart/new deploy.  If you need to persist uploaded/generated files between deploys you will need a &lt;a href="https://dev.to/docs/disks"&gt;Render Disk&lt;/a&gt;. &lt;br&gt; &lt;br&gt; While an ephemeral filesystem may feel counterintuitive at first – "why can't I just save my uploads to my instance?!" – it makes scaling and recovery from outages much more straightforward. Cloud-deployed instances benefit from being readily disposable/recreatable. &lt;br&gt; &lt;br&gt; When you do need to store longer-term data, you can add a &lt;a href="https://dev.to/docs/disks"&gt;Render Disk&lt;/a&gt; for file persistence. Depending on your data storage needs, it may be also worth considering a dedicated database like PostgreSQL or an object storage service like AWS S3. It’s easy to forget about this during development and end up with your application relying on local filesystem storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Custom domains and SSL/TLS&lt;/strong&gt;: Configure and secure your custom domain. You're unlikely to be running a dev environment on a custom domain or over HTTPS, so you may need to make some configuration updates to ensure your app is aware of how it may receive requests (&lt;a href="https://docs.djangoproject.com/en/4.0/ref/settings/#allowed-hosts" rel="noopener noreferrer"&gt;like ALLOWED_HOSTS in Django&lt;/a&gt;, etc.). You may also want to set up a redirect in your code to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301" rel="noopener noreferrer"&gt;301&lt;/a&gt; from the default service-name.onrender.com to your custom domain to keep it canonical. &lt;br&gt; &lt;br&gt; &lt;a href="https://dev.to/docs/tls"&gt;SSL/TLS Certificates are fully managed on Render&lt;/a&gt; and redirection of HTTP requests to HTTPS is automatic. You may need to ensure your assets use HTTPS in production if using a full URL and not just a relative path, to avoid any &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content/How_to_fix_website_with_mixed_content" rel="noopener noreferrer"&gt;mixed-content errors&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With all that said, using &lt;a href="https://dev.to/docs/docker"&gt;Docker&lt;/a&gt; can sometimes help reduce the burden of thinking about many of the above items by making your dev environment more closely match the production environment, but using Docker also adds a bunch of additional complexity, and that's a post for another day...&lt;/p&gt;

&lt;p&gt;We've touched on a lot of areas to get your project deployed successfully, but it’s hard to cover everything in a blog post. If you find you're still having trouble deploying things on Render, we're here to help and you can reach us at &lt;a href="mailto:support@render.com"&gt;support@render.com&lt;/a&gt; or post on &lt;a href="https://community.render.com/" rel="noopener noreferrer"&gt;community.render.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Migrate a Django App from Heroku to Render (and get it ready for prod)</title>
      <dc:creator>Audrey Maldonado</dc:creator>
      <pubDate>Fri, 01 Jul 2022 19:12:51 +0000</pubDate>
      <link>https://forem.com/render/migrate-a-django-app-from-heroku-to-render-and-get-it-ready-for-prod-596f</link>
      <guid>https://forem.com/render/migrate-a-django-app-from-heroku-to-render-and-get-it-ready-for-prod-596f</guid>
      <description>&lt;p&gt;&lt;strong&gt;By Rosalind Benoit - June 27, 2022&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Picture this: you run a hefty Django app, an online store for your company. It’s a collection of services – an API, a shiny frontend, a database, an in-memory datastore, worker nodes, a GUI for admins, a bunch of scheduled jobs…the whole shebang – all tuned to perform quickly for your customers. You’re ready to kick into high gear as the busy season approaches and the team preps new product launches.&lt;/p&gt;

&lt;p&gt;Then suddenly, your cloud provider is struggling with operational challenges. You can’t deploy updates. You’re channeling #HugOps, but your apps must continue to #JustWork.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1527293265149513736-877" src="https://platform.twitter.com/embed/Tweet.html?id=1527293265149513736"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1527293265149513736-877');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1527293265149513736&amp;amp;theme=dark"
  }



&lt;/p&gt;



&lt;p&gt;While our &lt;a href="https://render.com/docs/migrate-from-heroku" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; explains the process for moving any kind of app over, I often like to have a detailed example to follow. As a Python developer and a former Drupal and Magento admin for a retail chain, I wanted to share a migration post that could help someone in a time of need like the one described above. With this guide, I will help you move your Django app to Render so that it's production-ready : ) If you’re looking to start fresh instead, check out our guide for quickly &lt;a href="https://render.com/docs/deploy-django" rel="noopener noreferrer"&gt;deploying a new Django app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1527307407466106880-856" src="https://platform.twitter.com/embed/Tweet.html?id=1527307407466106880"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1527307407466106880-856');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1527307407466106880&amp;amp;theme=dark"
  }



&lt;/p&gt;



&lt;p&gt;This post will walk through a practical example of migrating an application from Heroku to Render using &lt;a href="https://saleor.io/" rel="noopener noreferrer"&gt;Saleor&lt;/a&gt;, a popular open-source e-commerce system. Based on Django and Python, Saleor has served high-volume companies in retail sectors like publishing and apparel since 2012 and is still growing rapidly. The latest major update introduces a modular front end powered by a GraphQL API and written with React and TypeScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Saleor, Why Render?
&lt;/h2&gt;

&lt;p&gt;Lots of Render users may encounter real-world use cases in which they need to deploy a full-featured storefront, and Saleor provides a flexible API-first solution. The framework appeals to us at Render because it’s developer-first, actively maintained, and receptive to community feedback and contribution.&lt;/p&gt;

&lt;p&gt;You may wonder why you would want to migrate your Django app from Heroku to Render. Our &lt;a href="https://render.com/render-vs-heroku-comparison" rel="noopener noreferrer"&gt;comparison page&lt;/a&gt; explains all the benefits you'll get – like private networking, HTTP/3, and DDoS protection, among many other things. Running a production-grade instance of Saleor on Heroku gets complicated really quickly. This guide explains how we addressed much of that complexity in the Render environment.&lt;/p&gt;

&lt;p&gt;There is one more reason we chose the Saleor project for this guide. We often hear questions about Docker Compose from our users interested in taking advantage of &lt;a href="https://render.com/docs/infrastructure-as-code" rel="noopener noreferrer"&gt;Render’s implementation of IaC&lt;/a&gt; (infrastructure-as-code). Within on of its repositories, the Saleor project maintains code and documentation for deploying all components of Saleor &lt;a href="https://github.com/saleor/saleor-platform/blob/main/docker-compose.yml" rel="noopener noreferrer"&gt;using Docker Compose&lt;/a&gt;. This guide will also discuss how we translated that &lt;code&gt;docker-compose.yml&lt;/code&gt; into a Render Blueprint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7lxfp95c1w8nuq0oeby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7lxfp95c1w8nuq0oeby.png" alt="A screenshot of the homepage of the Saleor Demo, an e-commerce site with Saleor-branded items for sale." width="800" height="991"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before embarking on a migration, it helps to understand how some high-level concepts map from Heroku to Render. Check out the helpful &lt;a href="https://render.com/docs/migrate-from-heroku#concept-mapping" rel="noopener noreferrer"&gt;concept mapping&lt;/a&gt; my coworker Chris put together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy Saleor on Render
&lt;/h2&gt;

&lt;p&gt;In the first part of this post, we’ll migrate a demo instance of Saleor from Heroku to Render. In the second half, we’ll productionalize Saleor on Render. If you want to follow along on our journey interactively, fork the &lt;a href="https://github.com/saleor/saleor" rel="noopener noreferrer"&gt;saleor repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll start by creating a &lt;code&gt;render.yaml&lt;/code&gt; file at the root of our repository. If our deployment is a Bob Ross creation, this is our blank canvas – the Blueprint that defines and integrates all the components of the working production application. You can certainly use the Render Dashboard to deploy services and databases individually, but we’ll codify the architecture in a &lt;code&gt;render.yaml&lt;/code&gt; so we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce the chance of human error&lt;/li&gt;
&lt;li&gt;Reduce repetitive point-and-click configuration&lt;/li&gt;
&lt;li&gt;Define a source of truth for the architecture with version control and git blame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Saleor’s README invitation to deploy their demo on a Heroku instance acts as our entry point for this post. We’ll first walk through each stage of migrating the demo to Render, and then illustrate steps for making the Django app production-ready. Saleor consists of three components: the Saleor Core backend server, the Saleor Dashboard GUI, and the Saleor React Storefront. Here's a preview of the architecture we'll deploy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gxk16djpkyd68tb4og8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gxk16djpkyd68tb4og8.png" alt="The Saleor architecture diagram" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ll paint each happy little tree at a time 🌳, but you can peek at the final creation &lt;a href="https://github.com/render-examples/saleor" rel="noopener noreferrer"&gt;on Github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Part One: Migrate a Django App from Heroku to Render&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Web Service for the API&lt;/li&gt;
&lt;li&gt;Add a Database&lt;/li&gt;
&lt;li&gt;Add Redis&lt;/li&gt;
&lt;li&gt;Add Build Steps&lt;/li&gt;
&lt;li&gt;Add a Frontend React App&lt;/li&gt;
&lt;li&gt;Add a Static Dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Part Two: Productionalize a Django App on Render&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a Secret File&lt;/li&gt;
&lt;li&gt;Add a Background Worker&lt;/li&gt;
&lt;li&gt;Add a Cron Job&lt;/li&gt;
&lt;li&gt;Update Build Steps&lt;/li&gt;
&lt;li&gt;Add Runtime Steps&lt;/li&gt;
&lt;li&gt;Add a Message Broker&lt;/li&gt;
&lt;li&gt;DRY It Up&lt;/li&gt;
&lt;li&gt;Help! (a Helper File for Derived Variables)&lt;/li&gt;
&lt;li&gt;Next Steps&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;&lt;strong&gt;A note on using other application manifests to generate Blueprints:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your application has been deployed on Heroku or with an infrastructure provisioning tool, you likely have other infrastructure-as-code files, like a &lt;code&gt;Procfile&lt;/code&gt; or &lt;code&gt;docker-compose.yml&lt;/code&gt;, to refer to while setting up a deployment to Render. These files can act as useful tools for creating Blueprints. This post will reference several alternative application manifests to demonstrate how they were used.&lt;/p&gt;

&lt;p&gt;The saleor and saleor-dashboard repositories both contain &lt;code&gt;app.json&lt;/code&gt; files for Heroku deployment. The Saleor project also includes the saleor-platform repository, which combines the Saleor API, saleor-dashboard, and the react-storefront. Both saleor-platform and the Heroku deployments are intended for local development and demo purposes and do not reflect productionalized Saleor environments. Still, we’ll use &lt;code&gt;app.json&lt;/code&gt; and Docker Compose files  as guides for getting started with a Blueprint that describes the Saleor services and their attributes. These resources are particularly helpful in pointing us to the environment variables and commands necessary to build and run an app.&lt;/p&gt;



&lt;h2&gt;
  
  
  Migrate a Django App from Heroku to Render
&lt;/h2&gt;

&lt;p&gt;To begin, we will deploy the Saleor Core API server using Render’s native Python environment. We maintain &lt;a href="https://render.com/docs/native-environments" rel="noopener noreferrer"&gt;native environments&lt;/a&gt; to make deploying to production similar in complexity to running code locally. Like Buildpacks in Heroku, they provide common language runtimes and minimize the need to provision utilities used to build and deploy; native environments aim to provide more control and customization capability while requiring fewer steps to use and understand than Buildpacks. Let’s jump in to creating a &lt;code&gt;render.yaml&lt;/code&gt; to set up our demo Django app!&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Web Service
&lt;/h3&gt;

&lt;p&gt;We’ll begin by defining a Web Service for the Django app’s GraphQL API in a brand new &lt;code&gt;render.yaml&lt;/code&gt;. This service is described in an &lt;a href="https://github.com/saleor/saleor/blob/main/app.json" rel="noopener noreferrer"&gt;&lt;code&gt;app.json file&lt;/code&gt;&lt;/a&gt; and corresponds to the &lt;a href="https://github.com/saleor/saleor-platform/blob/main/docker-compose.yml#L4-L33" rel="noopener noreferrer"&gt;&lt;code&gt;api&lt;/code&gt; service&lt;/a&gt; in the saleor-platform &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/render-examples/saleor&lt;/span&gt;
    &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip3 install -r requirements.txt &amp;amp;&amp;amp; python manage.py migrate --no-input&lt;/span&gt;
    &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALLOWED_HOSTS=".onrender.com" gunicorn --bind :$PORT --workers 4 --worker-class uvicorn.workers.UvicornWorker saleor.asgi:application&lt;/span&gt;
    &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor.settings&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NPM_CONFIG_PRODUCTION&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noreply@example.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;False&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;
        &lt;span class="na"&gt;generateValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PYTHON_VERSION&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.9.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s make sure each line is clear.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is a name for our service to make it easy to find on the Render Dashboard. It’s interchangeable with the &lt;code&gt;name&lt;/code&gt; in &lt;code&gt;app.json&lt;/code&gt; and is also used to generate an .onrender.com URL.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt; tells Render that we’d like to create a Web Service. When we’re deploying an API, the type may be either &lt;code&gt;web&lt;/code&gt; or &lt;code&gt;pserv&lt;/code&gt; (Private Service), depending on the use case.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt; specifies that we’d like to use Render’s native Python environment. This environment includes OS packages that common Python libraries need in addition to Python 3 specific environment variables.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repo&lt;/code&gt; specifies which repository we'll deploy from; update this if you're deploying your own fork of Saleor Core.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buildCommand&lt;/code&gt; tells Render which commands or files to run to build the Django app. Read more about &lt;code&gt;render-build.sh&lt;/code&gt; below. Heroku buildpacks often handle build elements behind the scenes, but Render encourages being more transparent and provides more control. Use the &lt;code&gt;RUN&lt;/code&gt; commands from builds described in Dockerfiles as a guide for build commands to include in a blueprint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;startCommand&lt;/code&gt; tells Render which commands or files to run to start the Django app. This command is &lt;a href="https://github.com/saleor/saleor/blob/main/Procfile#L2" rel="noopener noreferrer"&gt;specified in the &lt;code&gt;Procfile&lt;/code&gt;&lt;/a&gt; for a Heroku build. We'll prepend the command with the &lt;a href="https://docs.djangoproject.com/en/3.0/ref/settings/#allowed-hosts" rel="noopener noreferrer"&gt;&lt;code&gt;ALLOWED_HOSTS&lt;/code&gt;&lt;/a&gt; environment variable. Referencing the standard &lt;code&gt;RENDER_EXTERNAL_HOSTNAME&lt;/code&gt; variable will add our API's hostname to the &lt;a href="https://github.com/saleor/saleor/blob/main/saleor/settings.py#L408" rel="noopener noreferrer"&gt;list of allowed hosts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt; is an environment variable that &lt;a href="https://docs.djangoproject.com/en/4.0/topics/settings/#envvar-DJANGO_SETTINGS_MODULE" rel="noopener noreferrer"&gt;Django requires&lt;/a&gt; to determine which settings to use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEBUG&lt;/code&gt; is an environment variable that Django requires, and should never be set to &lt;code&gt;TRUE&lt;/code&gt; in &lt;a href="https://render.com/docs/deploy-django#go-production-ready" rel="noopener noreferrer"&gt;production deployments&lt;/a&gt;. We’ll use &lt;code&gt;DEBUG=True&lt;/code&gt; for our first demo deployment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEFAULT_FROM_EMAIL&lt;/code&gt; is an environment variable that Saleor uses to set a &lt;a href="https://docs.saleor.io/docs/2.11/developer/running-saleor/configuration#default_from_email" rel="noopener noreferrer"&gt;default email address for outgoing email&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/code&gt; is an environment variable that Saleor uses to control whether new account registration should require email confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SECRET_KEY&lt;/code&gt; is an environment variable that Django requires to &lt;a href="https://docs.djangoproject.com/en/3.0/ref/settings/#secret-key" rel="noopener noreferrer"&gt;provide cryptographic signing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PYTHON_VERSION&lt;/code&gt; is an environment variable used to &lt;a href="https://render.com/docs/python-version" rel="noopener noreferrer"&gt;customize the Python version&lt;/a&gt; for a project on Render if a version other than Render’s current default of 3.7 is required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Add a Database
&lt;/h3&gt;

&lt;p&gt;Now we need a PostgreSQL database for the app. Let's add that to our &lt;code&gt;render.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
&lt;span class="na"&gt;databases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-db&lt;/span&gt;
    &lt;span class="na"&gt;ipAllowList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# only allow connections from services in this Render account&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was simple, but how do we connect this database to the Web Service?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  - name: saleor
    type: web
    env: python
    repo: https://github.com/render-examples/saleor
    buildCommand: pip3 install -r requirements.txt &amp;amp;&amp;amp; python manage.py migrate --no-input
    startCommand: ALLOWED_HOSTS=".onrender.com" gunicorn --bind :$PORT --workers 4 --worker-class uvicorn.workers.UvicornWorker saleor.asgi:application
    envVars:
      - key: DJANGO_SETTINGS_MODULE
        value: saleor.settings
      - key: DEBUG
        value: True
      - key: NPM_CONFIG_PRODUCTION
        value: false
      - key: DEFAULT_FROM_EMAIL
        value: noreply@example.com
      - key: ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL
        value: False
      - key: SECRET_KEY
        generateValue: true
      - key: PYTHON_VERSION
        value: 3.9.0
      - key: DATABASE_URL
        fromDatabase:
          name: saleor-db
          property: connectionString

databases:
  - name: saleor-db
    ipAllowList: [] # only allow internal connections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The highlighted lines create an environment variable whose value is the connection string for the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Redis
&lt;/h3&gt;

&lt;p&gt;This Django app uses Redis for &lt;a href="https://docs.djangoproject.com/en/4.0/topics/cache/#redis" rel="noopener noreferrer"&gt;caching&lt;/a&gt;, so let's add Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# …snip…&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-redis&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
    &lt;span class="na"&gt;ipAllowList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# only allow connections from services in this Render account&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And similar to the database, we now need to tell the API how to access the Redis instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  - name: saleor
    type: web
      env: python
      repo: https://github.com/render-examples/saleor
      buildCommand: pip3 install -r requirements.txt &amp;amp;&amp;amp; python manage.py migrate --no-input
      startCommand: ALLOWED_HOSTS=".onrender.com" gunicorn --bind :$PORT --workers 4 --worker-class uvicorn.workers.UvicornWorker saleor.asgi:application
      envVars:
      - key: DJANGO_SETTINGS_MODULE
        value: saleor.settings
      - key: DEBUG
          value: True
      - key: NPM_CONFIG_PRODUCTION
          alue: false
      - key: DEFAULT_FROM_EMAIL
        value: noreply@example.com
      - key: ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL
        value: False
      - key: SECRET_KEY
        generateValue: true
      - key: DATABASE_URL
        fromDatabase:
          name: saleor-db
          property: connectionString
      - key: REDIS_URL
        fromService:
          type: redis
          name: saleor-redis
          property: connectionString
      - key: PYTHON_VERSION
        value: 3.9.0

databases:
    - name: saleor-db
      ipAllowList: [] # only allow internal connections
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The highlighted lines create an environment variable whose value is the connection string for the Redis instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Build Steps
&lt;/h3&gt;

&lt;p&gt;When Django apps are built and deployed, a few extra commands are commonly run to install dependencies and perform database migrations. Let’s create a &lt;code&gt;render-build.sh&lt;/code&gt; file in the root directory of our project. You can use this type of file to run any build steps your Django app requires.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="c"&gt;# exit on error&lt;/span&gt;

pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

python manage.py migrate &lt;span class="nt"&gt;--no-input&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of &lt;code&gt;buildCommand&lt;/code&gt; in the &lt;code&gt;render.yaml&lt;/code&gt; should be the path to the &lt;code&gt;render-build.sh&lt;/code&gt; file. With a build script in place, we can deploy the API, database, and Redis instance to ensure everything works as expected so far. Deploy the repository containing your &lt;code&gt;render.yaml&lt;/code&gt; as a Blueprint on Render. When it's done, go to the &lt;code&gt;/graphql&lt;/code&gt;/ path of your API's .onrender.com URL to see the GraphQL API Playground.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Dashboard
&lt;/h3&gt;

&lt;p&gt;With the API for our Django app scaffolded, we’re ready to define a frontend service to consume it. The Saleor platform deployed on Heroku includes the &lt;a href="https://github.com/saleor/saleor-dashboard" rel="noopener noreferrer"&gt;saleor-dashboard&lt;/a&gt;, so we’ll define this in our Blueprint next. It is a single-page Node.js dashboard app, &lt;a href="https://github.com/saleor/saleor-dashboard/blob/main/app.json" rel="noopener noreferrer"&gt;defined in an &lt;code&gt;app.json&lt;/code&gt; file&lt;/a&gt; for Heroku deployment, and uses Heroku’s static buildpack.&lt;br&gt;
When we migrate &lt;code&gt;saleor-dashboard&lt;/code&gt; to Render, we’ll use Render’s &lt;a href="https://render.com/docs/static-sites" rel="noopener noreferrer"&gt;static environment&lt;/a&gt;. This component of our infrastructure will run for free, since Static Sites are always free on Render, and Render will serve it over a global CDN with fully managed TLS certificates. Let’s add to our &lt;code&gt;render.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-dashboard&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;static&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/saleor/saleor-dashboard&lt;/span&gt;
    &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install &amp;amp;&amp;amp; API_URI="$TEMP_API_URI/graphql/" npm run build&lt;/span&gt;
    &lt;span class="na"&gt;staticPublishPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build/&lt;/span&gt;
    &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TEMP_API_URI&lt;/span&gt;
        &lt;span class="na"&gt;fromService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
          &lt;span class="na"&gt;envVarKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RENDER_EXTERNAL_URL&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;APP_MOUNT_URI&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dashboard/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;STATIC_URL&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dashboard/&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rewrite&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dashboard/index.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve added the commands for installing dependencies and building the app to add to our &lt;code&gt;buildCommand&lt;/code&gt;. The dashboard requires the &lt;code&gt;API_URI&lt;/code&gt; environment variable to build successfully. To avoid hard-coding, we'll derive a temporary environment variable from our API service using the &lt;code&gt;fromService&lt;/code&gt; attribute, and build our final &lt;code&gt;API_URI&lt;/code&gt; as part of our &lt;code&gt;buildCommand&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/saleor/saleor-dashboard/blob/main/Dockerfile" rel="noopener noreferrer"&gt;Dockerfile&lt;/a&gt; provides insight into the &lt;code&gt;staticPublishPath&lt;/code&gt; required for this project; we see a &lt;code&gt;COPY&lt;/code&gt; of the static contents to the /build/ directory, relative to the working directory. We also see the environment variables required to run the app in the Dockerfile, including the URI for our Saleor API and the URL where the dashboard should be mounted and served.&lt;/p&gt;

&lt;p&gt;To translate the configuration found in &lt;a href="https://github.com/saleor/saleor-dashboard/blob/main/static.json" rel="noopener noreferrer"&gt;static.json&lt;/a&gt; and the &lt;a href="https://github.com/saleor/saleor-dashboard/blob/main/nginx/default.conf" rel="noopener noreferrer"&gt;NGINX configuration file&lt;/a&gt; to Render, we add a &lt;code&gt;rewrite&lt;/code&gt; to route &lt;code&gt;/&lt;/code&gt; requests to &lt;code&gt;/dashboard/index.html&lt;/code&gt;. When we push these &lt;code&gt;render.yaml&lt;/code&gt; changes, Render will automatically create the new Static Site. &lt;/p&gt;

&lt;h3&gt;
  
  
  Add a React Storefront
&lt;/h3&gt;

&lt;p&gt;If I’m comparing this project to a painting, it's time we added something pretty. The final frontend piece of the Saleor platform, the storefront, is deployed to Heroku as part of the Saleor demo in its &lt;a href="https://github.com/saleor/saleor-storefront" rel="noopener noreferrer"&gt;original version&lt;/a&gt; of the storefront. This storefront is now deprecated because in 2021, the Saleor project added a new storefront service built with Next.js, TypeScript, and Tailwind CSS. The original &lt;code&gt;saleor-storefront&lt;/code&gt; can be migrated from Heroku similarly to the API and Dashboard projects, but Heroku deployment hasn’t been added to the new &lt;code&gt;react-storefront&lt;/code&gt; repository. The more modern react-storefront contains a &lt;a href="https://github.com/saleor/react-storefront/blob/main/Dockerfile.dev" rel="noopener noreferrer"&gt;development Dockerfile&lt;/a&gt; that provides clues to deployment requirements. In this guide, we’ll deploy the &lt;code&gt;react-storefront&lt;/code&gt; as the final component of our demo Saleor instance.&lt;/p&gt;

&lt;p&gt;Note that we can deploy the react-storefront on Render as-is using the Dockerfile. However, since Render provides an HTTP proxy for all Web Services, using nginx as defined in the Dockerfile is redundant. Instead, we can define our service more simply and use Render’s native NodeJS environment. We’ll use &lt;a href="https://github.com/saleor/saleor-platform/blob/main/docker-compose.yml#L35-L53" rel="noopener noreferrer"&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/a&gt;, the Dockerfile, and the &lt;a href="https://github.com/saleor/react-storefront/blob/main/package.json" rel="noopener noreferrer"&gt;&lt;code&gt;package.json&lt;/code&gt; file&lt;/a&gt; as starting points. Let’s add a storefront to our &lt;code&gt;render.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-storefront&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/saleor/react-storefront&lt;/span&gt;
    &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;pnpm install&lt;/span&gt;
      &lt;span class="s"&gt;&amp;amp;&amp;amp; NEXT_PUBLIC_API_URI=$TEMP_NEXT_PUBLIC_API_URL/graphql/ pnpm run build&lt;/span&gt;
      &lt;span class="s"&gt;&amp;amp;&amp;amp; pnpm run postbuild&lt;/span&gt;
    &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NEXT_PUBLIC_API_URI=$TEMP_NEXT_PUBLIC_API_URL/graphql/ pnpm start&lt;/span&gt;
    &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TEMP_NEXT_PUBLIC_API_URL&lt;/span&gt;
        &lt;span class="na"&gt;fromService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
          &lt;span class="na"&gt;envVarKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RENDER_EXTERNAL_URL&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NEXT_PUBLIC_IMAGE_CONVERSION_FORMATS&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;image/avif,image/webp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build command is &lt;a href="https://github.com/saleor/react-storefront/blob/main/Dockerfile.dev#L13" rel="noopener noreferrer"&gt;defined in the Dockerfile&lt;/a&gt;. The &lt;code&gt;docker-compose.yml&lt;/code&gt; and the Dockerfile only define &lt;code&gt;RUN&lt;/code&gt; commands for a development instance, but we’ll run the &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, and &lt;code&gt;postbuild&lt;/code&gt; scripts defined in the &lt;a href="https://github.com/saleor/react-storefront/blob/main/package.json#L8-L19" rel="noopener noreferrer"&gt;package.json&lt;/a&gt; using &lt;a href="https://pnpm.io/cli/run" rel="noopener noreferrer"&gt;pnpm run&lt;/a&gt; commands.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;environment:&lt;/code&gt; clause of the &lt;a href="https://github.com/saleor/saleor-platform/blob/main/docker-compose.yml#L51-L53" rel="noopener noreferrer"&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;/a&gt; provides clues to the minimum environment variables required for react-storefront to run. &lt;code&gt;NEXT_PUBLIC_API_URI&lt;/code&gt; is required to define the location of the GraphQL API, and we’ve set this to our Saleor API instance’s public URI with &lt;code&gt;/graphql/&lt;/code&gt; added per &lt;a href="https://github.com/saleor/react-storefront/blob/main/docs/configuration.md" rel="noopener noreferrer"&gt;react-storefront docs&lt;/a&gt;. In our testing, the only other environment variable required for successful deployment was &lt;code&gt;NEXT_PUBLIC_IMAGE_CONVERSION_FORMATS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🎇 Get excited! 🎆 With a Dashboard and Storefront added to our Blueprint, we are ready to deploy our demo instance of Saleor. Pushing these changes will automatically deploy the storefront. Try it out if you're following along in your own repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Productionalizing a Django App on Render
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, while the deploy-to-Heroku resources that Saleor provides are suitable for a demo application, we would need to modify them to deploy a production-grade instance of the Saleor platform to Heroku. Instead, we’ll walk through productionalizing our Django e-commerce app on Render. We’ll now frame and hang our painting – or maybe you prefer imagining the addition of a majestic mountain or waterfall. Either way, now that we’re beyond Heroku, keep reading to tap some handy Render features, add power, and DRY it up. We'll finish with a hardened Saleor instance impervious to "happy accidents."&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Secret File
&lt;/h3&gt;

&lt;p&gt;A production-grade instance of Saleor needs an &lt;a href="https://docs.saleor.io/docs/3.x/developer/running-saleor/configuration#rsa_private_key" rel="noopener noreferrer"&gt;RSA private key&lt;/a&gt;. Render’s Secret Files can help us securely store this key. To begin, we need to generate a key in the specified PEM format. We can do this locally: &lt;code&gt;ssh-keygen -t rsa -b 4096 -m PEM&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When generating the key, we should give it a unique name like &lt;code&gt;saleor-key&lt;/code&gt; to avoid overwriting any existing SSH keys. No passphrase is required. With the newly-generated private key copied to the clipboard, we can navigate to our Saleor Core (API) Web Service in the Render Dashboard and create a Secret File. In &lt;strong&gt;Environment&lt;/strong&gt; &amp;gt; &lt;strong&gt;Secret Files&lt;/strong&gt;, we can add a Secret File by providing the filename as the key and pasting the private key into Contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdb6u1lynf8hh9yke00zg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdb6u1lynf8hh9yke00zg.png" alt="A screenshot of the secret file dialog in The Render Dashboard, found in Environment &amp;gt; Secret Files" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our private key is accessible at the absolute path &lt;code&gt;/etc/secrets/saleor-key&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Background Worker
&lt;/h3&gt;

&lt;p&gt;In processing web requests to your Django app, it’s often useful to offload tasks to an asynchronous background worker. Our Heroku instance of Saleor used a &lt;code&gt;celeryworker&lt;/code&gt; dyno, and we can provision a similar celery worker on Render. Let’s add a celery worker to our &lt;code&gt;render.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-worker&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/render-examples/saleor&lt;/span&gt;
  &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;standard&lt;/span&gt;
  &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./render-build.sh&lt;/span&gt;
  &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A saleor --app=saleor.celeryconf:app worker --loglevel=info -E&lt;/span&gt;
  &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
      &lt;span class="na"&gt;fromDatabase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-db&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connectionString&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REDIS_URL&lt;/span&gt;
      &lt;span class="na"&gt;fromService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-redis&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connectionString&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PYTHON_VERSION&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.9.0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor.settings&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NPM_CONFIG_PRODUCTION&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noreply@example.com&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;False&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;
      &lt;span class="na"&gt;generateValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;plan: standard&lt;/code&gt; attribute is set because the celery worker requires more memory than what's availabe in free plans. Also, adding the same environment variables that our API process uses creates some duplication in our &lt;code&gt;render.yaml&lt;/code&gt;, which we’ll also address in a later section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Cron Job
&lt;/h3&gt;

&lt;p&gt;Saleor runs a command periodically to update currency exchange rates for sellers doing business internationally. Saleor’s demo app uses the Heroku Scheduler to run this daily. We can use a Cron Job to keep the exchange rates up to date. Let’s create a Render Cron Job to run the &lt;code&gt;python manage.py update_exchange_rates --all&lt;/code&gt; command at 1 AM every day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cron&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;exchange-rates&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python manage.py update_exchange_rates --all&lt;/span&gt;
  &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./render-build.sh&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/render-examples/saleor&lt;/span&gt;
  &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor.settings&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NPM_CONFIG_PRODUCTION&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noreply@example.com&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;False&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;
      &lt;span class="na"&gt;generateValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PYTHON_VERSION&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.9.0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
      &lt;span class="na"&gt;fromDatabase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-db&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connectionString&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REDIS_URL&lt;/span&gt;
      &lt;span class="na"&gt;fromService&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-redis&lt;/span&gt;
        &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connectionString&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OPENEXCHANGERATES_API_KEY&lt;/span&gt;
      &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice that the definition of the Cron Job looks very similar to that of the Web Service for our API. A Render Cron Job is not tied to a parent app or Web Service like Heroku’s Scheduler add-on. It is defined on its own. A Cron Job builds and runs code from any repository on a specified schedule, whereas the Heroku Scheduler can only execute a command using an existing deployed app. In this case, similarly to the background worker, the Cron Job needs to use many of the same environment variables as its fellow services, and we’ll address that duplication below.&lt;/p&gt;

&lt;p&gt;A few other things to note about this Cron Job definition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;schedule&lt;/code&gt; property; &lt;code&gt;schedule&lt;/code&gt; is a cron expression that defines when to run the job – in this case, we’re running it every day at 1 am UTC.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;OPENEXCHANGERATES_API_KEY&lt;/code&gt; is required to get exchange rates from the Open Exchange Rates API. You’ll need to sign up for a free Open Exchange Rates account to enable the integration. The value for your &lt;code&gt;OPENEXCHANGERATES_API_KEY&lt;/code&gt; should be your Open Exchange Rates app ID. Read more in the &lt;a href="https://docs.saleor.io/docs/2.11/developer/running-saleor/background-tasks#fetching-currency-conversion-rates" rel="noopener noreferrer"&gt;Saleor documentation&lt;/a&gt;. Because this value should probably be kept secret, you should define it with the placeholder &lt;code&gt;sync: false&lt;/code&gt; so that you can add teh value securely in the dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that, our Cron Job is in place to keep prices in local currencies updated, and we are fleshing out a promising landscape.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Build Script
&lt;/h3&gt;

&lt;p&gt;Now, let’s update our build script to support the secret key and services we’ve added to get our instance of Saleor ready for production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="c"&gt;# exit on error&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RSA_PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/secrets/saleor-key&lt;span class="si"&gt;)&lt;/span&gt;

pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RENDER_SERVICE_TYPE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;python manage.py migrate &lt;span class="nt"&gt;--no-input&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we set the &lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt; environment variable to make it available in the build context.&lt;/p&gt;

&lt;p&gt;We also add logic that checks the &lt;a href="https://render.com/docs/environment-variables" rel="noopener noreferrer"&gt;standard Render environment variable&lt;/a&gt; &lt;code&gt;RENDER_SERVICE_TYPE&lt;/code&gt; to ensure that database migration only runs for the Web Service. This change helps us to avoid a race condition which would prevent one of the services from deploying successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Runtime Script
&lt;/h3&gt;

&lt;p&gt;Next, we’ll replace our original &lt;code&gt;startCommand:&lt;/code&gt; in the Python services with a start script to accommodate our more complex deployment. We’ll use a subcommand to address the cases of starting the celery worker and cron job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="c"&gt;# exit on error&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RSA_PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/secrets/saleor-key&lt;span class="si"&gt;)&lt;/span&gt;


&lt;span class="nv"&gt;subcommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$subcommand&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;server&lt;span class="p"&gt;)&lt;/span&gt;
    gunicorn &lt;span class="nt"&gt;--bind&lt;/span&gt; :&lt;span class="nv"&gt;$PORT&lt;/span&gt; &lt;span class="nt"&gt;--workers&lt;/span&gt; 4 &lt;span class="nt"&gt;--worker-class&lt;/span&gt; uvicorn.workers.UvicornWorker saleor.asgi:application
    &lt;span class="p"&gt;;;&lt;/span&gt;
  worker&lt;span class="p"&gt;)&lt;/span&gt;
    celery &lt;span class="nt"&gt;-A&lt;/span&gt; saleor &lt;span class="nt"&gt;--app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;saleor.celeryconf:app worker &lt;span class="nt"&gt;--loglevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;info &lt;span class="nt"&gt;-E&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
  cron&lt;span class="p"&gt;)&lt;/span&gt;
    python3 manage.py update_exchange_rates &lt;span class="nt"&gt;--all&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unknown subcommand"&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We explicitly set the &lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt; environment variable since it is also required in Saleor’s run context. We also need a subcommand to tell the script to start either the web server or the celery worker. To finish this step, we’ll update the &lt;code&gt;startCommand&lt;/code&gt; value for our Python services in &lt;code&gt;render.yaml&lt;/code&gt; to reference the new start script; for our API, we’ll use &lt;code&gt;./render-start.sh server&lt;/code&gt;, for our worker, &lt;code&gt;./render-start.sh worker&lt;/code&gt;, and for our cron job, &lt;code&gt;./render-start.sh cron&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add CloudAMQP
&lt;/h3&gt;

&lt;p&gt;The Heroku deployment of Saleor uses CloudAMQP as an add-on for message brokering. To use CloudAMQP with our Render deployment, we first create a free &lt;a href="https://www.cloudamqp.com/" rel="noopener noreferrer"&gt;CloudAMQP&lt;/a&gt; instance and note the URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuw5g8vkgypzgofqjf69j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuw5g8vkgypzgofqjf69j.png" alt="A screenshot of the CloudAMQP dashboard, indicating the URL that should be used as an environment variable." width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we set the value of the &lt;code&gt;CLOUDAMQP_URL&lt;/code&gt; environment variable to the URL provided by the CloudAMQP dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery-worker&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
  &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/render-examples/saleor&lt;/span&gt;
  &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;standard&lt;/span&gt;
  &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./render-build.sh&lt;/span&gt;
  &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./render-start.sh worker&lt;/span&gt;
  &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# …snip…&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CLOUDAMQP_URL&lt;/span&gt;
      &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DRY It Up
&lt;/h3&gt;

&lt;p&gt;As I mentioned previously, there’s some duplication in the &lt;code&gt;render.yaml&lt;/code&gt; between the Python services. On Render, we can add a happy little cloud to our environment – an &lt;a href="https://render.com/docs/configure-environment-variables#2-environment-groups" rel="noopener noreferrer"&gt;Environment Group&lt;/a&gt; – to reduce some of this duplication. An Environment Group is a set of environment variables maintained in one place and shared with multiple services. Let’s move the &lt;code&gt;SECRET_KEY&lt;/code&gt;, &lt;code&gt;PYTHON_VERSION&lt;/code&gt;, &lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt;, &lt;code&gt;DEBUG&lt;/code&gt;, &lt;code&gt;NPM_CONFIG_PRODUCTION&lt;/code&gt;, &lt;code&gt;DEFAULT_FROM_EMAIL&lt;/code&gt;, and &lt;code&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/code&gt; environment variables to an Environment Group. We're also going to add &lt;code&gt;ALLOWED_CLIENT_HOSTS&lt;/code&gt;, which is required when we flip &lt;code&gt;DEBUG&lt;/code&gt; from &lt;code&gt;True&lt;/code&gt; to &lt;code&gt;False&lt;/code&gt; in the next section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# …snip…&lt;/span&gt;
&lt;span class="na"&gt;envVarGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor-settings&lt;/span&gt;
    &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PYTHON_VERSION&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.9.0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DJANGO_SETTINGS_MODULE&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saleor.settings&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NPM_CONFIG_PRODUCTION&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_FROM_EMAIL&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noreply@example.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENABLE_ACCOUNT_CONFIRMATION_BY_EMAIL&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;False&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;
        &lt;span class="na"&gt;generateValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALLOWED_CLIENT_HOSTS&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.onrender.com&lt;/span&gt; &lt;span class="c1"&gt;# If using custom domains, change this to a comma-separated list of your storefront and dashboard hostnames&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can remove the definition of those environment variables from the Python services and add the following property to their envVars objects:&lt;br&gt;
&lt;code&gt;- fromGroup: saleor-settings&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To make the most of our environment group, note that we can also add our &lt;code&gt;saleor-key&lt;/code&gt; secret file to our environment group so it’s accessible from each service’s environment. We need to do this in the Dashboard after the Environment Group creation is complete.&lt;/p&gt;
&lt;h3&gt;
  
  
  Help! (a Helper File for Derived Variables)
&lt;/h3&gt;

&lt;p&gt;Our repetition of the &lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt; export is another source of duplication in our deployment code. In addition to &lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt;, Django apps and Saleor in particular also require a few other security-focused environment variables to run in production: the aforementioned &lt;a href="https://docs.djangoproject.com/en/3.0/ref/settings/#allowed-hosts" rel="noopener noreferrer"&gt;&lt;code&gt;ALLOWED_HOSTS&lt;/code&gt;&lt;/a&gt;, used to thwart HTTP Host header attacks, and &lt;a href="https://docs.saleor.io/docs/3.x/developer/running-saleor/configuration#allowed_client_hosts" rel="noopener noreferrer"&gt;&lt;code&gt;ALLOWED_CLIENT_HOSTS&lt;/code&gt;&lt;/a&gt;, used to restrict API access to the target clients you define. To continue our DRYing kick and make these parameters centrally available, we can use a helper script and source it in both our &lt;code&gt;render-build.sh&lt;/code&gt; and &lt;code&gt;render-start.sh&lt;/code&gt; scripts. Let’s create a helper script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="c"&gt;# exit on error&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RSA_PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/secrets/saleor-key&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ALLOWED_HOSTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$RENDER_EXTERNAL_HOSTNAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can replace the &lt;code&gt;export&lt;/code&gt; lines in both &lt;code&gt;render-build.sh&lt;/code&gt; and &lt;code&gt;render-start.sh&lt;/code&gt; with &lt;code&gt;source helpers/variables.sh&lt;/code&gt; and add any other programmatically derived environment variables to &lt;code&gt;helpers/variables.sh&lt;/code&gt; in future. &lt;/p&gt;

&lt;p&gt;To complete our de-duplication effort, we should add our &lt;code&gt;RSA_PRIVATE_KEY&lt;/code&gt; to the Environment Group we created so it's shared among our Environment Group. We can do this in the Dashboard in &lt;strong&gt;Env Groups&lt;/strong&gt; &amp;gt; &lt;strong&gt;saleor&lt;/strong&gt; &amp;gt; &lt;strong&gt;Secret Files&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Drumroll, please: our happy little services, scripts, and tools now depict a complete landscape. With our production-ready environment variables now available in both the build and start contexts for all of our services, we are ready to flip the switch on our Saleor services in &lt;code&gt;render.yaml&lt;/code&gt; to &lt;code&gt;DEBUG=False&lt;/code&gt;, sync our Blueprint, and deploy!!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freaqiy2j6dv1jqwvw992.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freaqiy2j6dv1jqwvw992.png" alt="A screenshot of an item detail page in the Saleor Demo, with a Saleor-branded shirt for sale." width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Bob Ross warned us, “If you do too much, it’s going to lose its effectiveness." At Render, we do a lot so that &lt;em&gt;you&lt;/em&gt; can do just enough to build great things. If you're thinking about migrating your Django project to Render, you’re considering how to translate each component to the new environment, and the amount of work required. This post aims to provide good coverage of those tasks, but there is always room for growth : ) We’ll keep building, and in the meantime, we (and your fellow developers) would love to see &lt;em&gt;your&lt;/em&gt; solutions for the following next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You may &lt;a href="https://render.com/docs/deploy-rabbitmq" rel="noopener noreferrer"&gt;deploy RabbitMQ&lt;/a&gt; as a Private Service on Render instead of using an external RabbitMQ-as-a-service provider. A version of this project with a RabbitMQ instance added to the Blueprint would remove the need to sign up and pay the bill for another service. If you extend the project as such, please post in &lt;a href="https://community.render.com/c/render-examples/12" rel="noopener noreferrer"&gt;Render-Examples on the Render Community Site&lt;/a&gt; to share your work.&lt;/li&gt;
&lt;li&gt;At Render, when we think about what to paint in our next masterpiece, we always start with user feedback. Unsurprisingly, object storage is one of our most common &lt;a href="https://feedback.render.com/features/p/cloud-object-storage" rel="noopener noreferrer"&gt;product requests&lt;/a&gt; (upvote plz!) While we work on that, you can &lt;a href="https://docs.saleor.io/docs/3.x/developer/running-saleor/s3" rel="noopener noreferrer"&gt;use S3&lt;/a&gt; to store and serve static files for your Saleor instance.&lt;/li&gt;
&lt;li&gt;If you’re migrating an existing Django application from Heroku to Render, you likely have data in a database that you’ll need to export and import. We heard from readers and Render users that a detailed guide for these steps would provide critical context to support real migrations. Given their importance, we will dedicate a full blog post to those steps. Look out for a language and framework-agnostic guide to the data export and migration process in an upcoming post. Feel free to share about your own experience with data migration.&lt;/li&gt;
&lt;li&gt;This post touches on translating &lt;code&gt;app.json&lt;/code&gt; and &lt;code&gt;docker-compose.yml&lt;/code&gt; files to &lt;code&gt;render.yaml&lt;/code&gt;, but there’s more to say on the subject. We dream of &lt;a href="https://render.canny.io/features/p/support-docker-compose" rel="noopener noreferrer"&gt;automating this&lt;/a&gt; for our users. In the meantime, our community would love to read about your translation tricks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can’t wait to hear from you about whether this guide helped you migrate your application. If you’ve considered moving a Django app over from Heroku, or if you use Saleor or another open source e-commerce platform on Render, we’d love to see what you’ve built out. Post on your channel of choice, reach out on our &lt;a href="https://community.render.com/" rel="noopener noreferrer"&gt;community site&lt;/a&gt;, or &lt;a href="https://twitter.com/dnilas0r" rel="noopener noreferrer"&gt;hop into my DMs&lt;/a&gt; with your big ideas, or the unvarnished truth. We’re joined at the hip with our developer community and trust me, we want to hear from you!&lt;/p&gt;




&lt;p&gt;Rosalind Benoit leads Developer Marketing at Render. She enjoys cycling, rock climbing, and Haruki Murakami novels. You can contact Rosalind at &lt;a href="https://twitter.com/dnilas0r" rel="noopener noreferrer"&gt;@dnilas0r&lt;/a&gt; or &lt;a href="mailto:rosalind@render.com"&gt;rosalind@render.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>cloud</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
