How to Deploy a Flask App to Heroku With SSL and Redis Cache
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Deployment
Now that we have a functional web application our next step is to get it live on the internet. Earlier in the book, we covered the steps involved to deploy our simple API application to Heroku, but Yumroad is a bit more complicated in that it has a database, has job workers for delayed jobs, uses a Redis cache, and needs SSL.
In order for our application to be accesible to end users, we need to configure each of the pieces of our infrastructure.

We will need to configure each of these pieces in order to get our application up and running.
Which setup should I use?
When you are starting off, the best choice is to choose an infrastructure design that will require the least mental overhead and will allow you to focus on your application. For most developers, the choice when you are starting off would be to platform as a service provider.
If you're a new developer and haven't had much infrastructure experience, you are certainly better off using a PaaS (platform as a service) provider like Heroku. Platforms like Heroku make it easy to setup servers, databases, and scale up your application. You will not need to worry as much about how to deploy new versions or if the underlying servers go down. The free plans are often enough to test your applications and run lightweight applications. When you are ready, you can pay for more resources as you go to improve performance.
If you are working on a much larger project, you may find that Heroku is not as economical if your application needs a lot of resources or is performance sensitive. In that case, you should look at manually configuring servers. If you do that, you will need to manage the servers, the configuration, and how you will have to think about getting new versions of the code updated on the servers.
Heroku
Setup
Create an account on Heroku and download the Heroku CLI if you haven't already done that.
To install the Heroku command line interface, you can go to the download page available at https://devcenter.heroku.com/articles/heroku-cli and walk through the installer for your operating system.
You will also need to have "git", a version tracking software tool, installed. In Ubuntu (or Ubuntu under Windows Subsytem for Link) you can install it with sudo apt-get install git
. On a Mac, you can use homebrew
to install git
by running brew install git
in your terminal.
Our first step is to create an application on Heroku and set up our folder to point to that app. In your Heroku dashboard, create a new application and specify a name. In our example, we'll use yumroad-prod
, but you can use any available name.

Now that we have an application, we'll also want to configure some of the other infrastructure pieces like our database and Redis cache. In the resources tab of the Heroku dashboard for our application, find the add ons section and add the "Heroku Postgres" add on. You can use the free hobby plan for now.

Why Postgres? It's a personal preference. Any good relational database supported by SQLAlchemy like MySQL will work.
Once you have installed Heroku Postgres, you can tweak settings and view credentials if you need them on the datastore dasbhoard, which you can access through the resources tab.

Next we'll repeat the process for Redis by installing "Heroku Redis"

Everything we've done on the Heroku dasbhoard to date is possible to do through the CLI as well.
Next up we'll want to configure the secrets for our production environment (the ones specified in yumroad/config.py
). At this point, our configuration file should look like this:
import os
folder_path = os.path.abspath(os.path.dirname(__file__))
class BaseConfig:
TESTING = False
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = os.getenv('YUMROAD_SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')
MAIL_SERVER = os.getenv('MAIL_SERVER', 'smtp.mailgun.org')
MAIL_PORT = os.getenv('MAIL_SERVER_PORT', 2525)
MAIL_USERNAME = os.getenv('MAIL_USERNAME')
MAIL_USE_TLS = os.getenv('MAIL_USE_TLS', True)
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
SENTRY_DSN = os.getenv('SENTRY_DSN')
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY', 'sk_test_k1')
STRIPE_PUBLISHABLE_KEY = os.getenv('STRIPE_PUBLISHABLE_KEY', 'sk_test_k1')
STRIPE_WEBHOOK_KEY = os.getenv('STRIPE_WEBHOOK_KEY', 'sk_test_k1')
REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
RQ_REDIS_URL = REDIS_URL
RQ_DASHBOARD_REDIS_URL = RQ_REDIS_URL
class DevConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(folder_path, 'dev.db'))
SECRET_KEY = os.getenv('YUMROAD_SECRET_KEY', '00000abcdef')
SQLALCHEMY_ECHO = True
# Don't do anything fancy with the assets pipeline (faster + easier to debug)
ASSETS_DEBUG = True
RQ_REDIS_URL = REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
CACHE_TYPE = 'simple'
DEBUG_TB_INTERCEPT_REDIRECTS = False
class TestConfig(BaseConfig):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(folder_path, 'test.db'))
SECRET_KEY = os.getenv('YUMROAD_SECRET_KEY', '12345abcdef')
WTF_CSRF_ENABLED = False
STRIPE_WEBHOOK_KEY = 'whsec_test_secret'
ASSETS_DEBUG = True
# Run jobs instantly, without needing to spin up a worker
RQ_ASYNC = False
RQ_CONNECTION_CLASS = 'fakeredis.FakeStrictRedis'
DEBUG_TB_ENABLED = False
CACHE_TYPE = 'null'
CACHE_NO_NULL_WARNING = True
class ProdConfig(BaseConfig):
DEBUG = False
SESSION_PROTECTION = "strong"
# You should be using HTTPS in production anyway, but if you are not, turn
# these two off
SESSION_COOKIE_SECURE = True
REMEMBER_COOKIE_SECURE = True
ASSETS_DEBUG = False
RQ_REDIS_URL = REDIS_URL = os.getenv('REDIS_URL')
RQ_ASYNC = (REDIS_URL is not None)
CACHE_TYPE = 'redis'
CACHE_KEY_PREFIX = 'yumroad-'
configurations = {
'dev': DevConfig,
'test': TestConfig,
'prod': ProdConfig,
}
Our production environment in config.py
, will looking for a few things: YUMROAD_SECRET_KEY
, DATABASE_URL
, MAIL_USERNAME
, MAIL_PASSWORD
, SENTRY_DSN
, STRIPE_SECRET_KEY
, STRIPE_PUBLISHABLE_KEY
, STRIPE_WEBHOOK_KEY
, REDIS_URL
.
To set config variables, go to the settings tab of your application on the Heroku dashboard and click on "Reveal Config Vars".

You will see that two config variables have already been set. The add ons we installed earlier each set up one config variable that provides the connection URL for their respective service. These environment variables already properly map to the environment variables we had BaseConfig
lookup up (DATABASE_URL
and REDIS_URL
).
For the other secrets, we'll want to set those on the Heroku dashboard. For the application SECRET_KEY
. which is set by the YUMROAD_SECRET_KEY
environment variable, we should generate a new random value for production. We can use Python to generate a random string by running the following command in our terminal: python -c "import os; print(os.urandom(24).hex())"
and using that value for YUMROAD_SECRET_KEY
In addition, you will also want to set FLASK_APP
to yumroad:create_app('prod')
and FLASK_ENV
to production
.

Deployment
Now that the base infrastructure is set up, we'll have to tell Heroku how to run our code. Heroku works best with Git to track new versions of the application to deploy. This form of deployment makes it easy to track changes between deploys, revert versions quickly, and deploy from anywhere with a single command. It is a good idea to start projects off by creating a Git repository from the beginning so you can always keep track of changes.
First, we'll need to setup a git project within our application. Within the root folder of our application (the folder that requirements.txt
and the yumroad
folder is located in), we will run git init
to set up a new git project just for this folder.
$ cd our-project
$ ls
env requirements.txt tests conftest migrations seed.py yumroad
$ git init
Initialized empty Git repository in /home/our-project/.git/
This lesson preview is part of the Fullstack Flask: Build a Complete SaaS App with Flask course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack Flask: Build a Complete SaaS App with Flask with a single-time purchase.

[00:00 - 00:14] Now we're talking about how to deploy our application into a production environment. We have a functional web application that's been tested and we're actually going to need a lot more things to get our application deployed.
[00:15 - 00:26] We have to have a Redis cache, we need to have a database, we need to have SSL in front of our application to be able to use Stripe in production mode. So there's just a variety of things here.
[00:27 - 00:39] So this diagram covers some pieces of it. So we're going to have to have a database, we're going to have to have Redis, we're going to have to have Python, probably Guna Corn as a WSTI server, as a in front of our application code.
[00:40 - 00:46] And we're going to have to have something in front of that to serve it to the internet. And then we're going to have to have a worker separately as well.
[00:47 - 00:56] So it's not a trivial piece of infrastructure to get this going. So what we're going to need is to choose a place to deploy our application.
[00:57 - 01:07] There's generally two choices here. Either you can use an infrastructure provider that will give you a platform as a service or you can just try and work with raw virtualized servers.
[01:08 - 01:25] If you're a new developer and haven't had much infrastructure experience, you 're still only better off using a platform as a service provider like Heroku. Platforms like Heroku make it easy to set up servers, databases and scale your application and you won't need to worry about as much about how to deploy new versions of applications or if the underlying servers themselves go down.
[01:26 - 01:34] The free plans are often enough to test your application and run lightweight applications. When you're ready, you can pay for more resources as you go to improve performance.
[01:35 - 01:46] Now if you're working on a much larger project, you might find that Heroku is not economical if your application actually needs a lot of resources or is particularly performance sensitive. In that case, you should look at manually configuring your own servers.
[01:47 - 01:56] If you do that, you're going to need to manage the servers, the configuration and you'll also have to think about how to get new versions of your code updated on the servers. So we're going to talk about Heroku in our case.
[01:57 - 02:03] So the first step you're going to have to do is set up a new account on Heroku. You can do that on Heroku's website.
[02:04 - 02:18] I'm then going to go ahead and click on Create New Application and here I'm presented with a way to set up a new application. So what I'm going to do is I'm going to call mine yum.org-app and see if we can use that.
[02:19 - 02:24] Perfect. So I'm going to go in and create app and now we see all of these configuration instructions.
[02:25 - 02:32] So we're going to use Heroku Git as our way to deploy. So we're going to push our Git repository that we've been tracking all on to Heroku.
[02:33 - 02:42] Next up is you're going to have to install the Heroku CLI. So the way you can install the Heroku CLI is by following the instructions here .
[02:43 - 02:51] Now, going back on a Bluetooth, you can use this instruction as well as some other ways to do it. This is another prep so you can install it as well.
[02:52 - 03:05] Once you get the Heroku CLI up and running, you can then run Heroku login and you're going to be able to then push your application to Heroku. We're also going to have to set some configuration stuff in the meantime.
[03:06 - 03:10] Okay. So I've installed the Heroku CLI here and then I'm going to run Heroku login.
[03:11 - 03:23] This is going to go ahead and open up the browser here and this is going to allow me to login through my browser. And then once I've logged in, it says that I'm logged in as my, you know, was perfect.
[03:24 - 03:29] Now we're going to run through the next steps on the Heroku guide. First off, it's going to tell us to initialize a Git repository.
[03:30 - 03:35] Now we've already done that. So let's go back in our project and run Git status.
[03:36 - 03:46] So I've already committed all my changes, but you might need to add the current changes here like this and then run git commit dash M, my changes. Okay.
[03:47 - 03:59] To deploy your code, essentially all you have to do is add Heroku as a Git remote. And so the way I'm going to run that is I'm going to run Heroku Git remote dash a Yum Road app.
[04:00 - 04:06] Okay. Now, before we deploy our code, let's go and configure a few things on Heroku.
[04:07 - 04:12] For example, we're going to need a database on Heroku. So let's go back here and let's configure some addons.
[04:13 - 04:20] So here on resources, we can go ahead and add some addons. So for example, I'm going to need Postgres.
[04:21 - 04:36] So I'm going to configure Heroku Postgres and the free plan is fine for me. Similarly, I'm going to add Heroku Redis because we're going to need a Redis server.
[04:37 - 04:58] You can view the details of how Heroku Redis works and some of the restrictions on the free version here, as well as some of the configuration information. Going back to the config, once we've added Heroku and Redis, we're going to also want to add some of the configuration variables we've created in config.py.
[04:59 - 05:12] So if we go back into our code, we can see that there's a bunch of variables we 'll need to set. For example, mail username, mail password, the strike secret key, the secrets, and some additional information as well.
[05:13 - 05:20] So we're going to need to configure that in the Heroku dashboard. Let's go ahead and do that.
[05:21 - 05:31] Here I'm going to go into settings and I'm going to go on reveal config. Here is where all of our database and Redis URLs are already pre-configured from our extension.
[05:32 - 05:37] Now our application looks at those two, so those two are taken care of. Now we're going to have to define some other things.
[05:38 - 05:48] For example, our Sentry DSN. And we're also going to have to define a stripe secret key.
[05:49 - 05:54] So I'm going to go ahead and define a bunch of these and I'll get back to you in a bit. For some of these, we're going to have to create some new data.
[05:55 - 06:13] So for example, for this, we might need to set up the webhook URL so we say Web hooks are a stripe and we can change this later and we can choose what events to do. So we can check out a question completed.
[06:14 - 06:23] And we can add a webhook here and then it's going to give us a webhook signing key after we enter our password. So here you can see the signing secret.
[06:24 - 06:36] And so when we go back in our config settings, we're going to want to set that like so. Okay, at this point, I've configured all of our settings.
[06:37 - 06:46] We're going to go ahead and create in the proc file something to run our web server. So Heroku is going to be listening on an environment variable that's specified here.
[06:47 - 06:53] It's going to be listening on that port. And then we're going to tell Gunacorn to run our project by running this command.
[06:54 - 07:04] So that specifies the web worker that Heroku is going to be looking for. We'll also want to specify a worker as a separate type of machine that we want to be running.
[07:05 - 07:13] So to specify that Marker, we just tell Heroku what command we want to run there. Then in app.json, we're going to have to paste in a bunch of descriptive text there.
[07:14 - 07:21] So let's go ahead and take a look at what we might want to do. Here's an example app.json.
[07:22 - 07:25] So I've pasted a bunch of information here. Most of this information is not important.
[07:26 - 07:37] But the thing we do want to know is that there's this pre deploy script. And so this pre deploy script is going to tell Flask assets to build our assets before our application starts.
[07:38 - 07:45] And this is important because otherwise on the first request, Flask assets is going to try to build all of our assets. And that's going to delay the page load for the first user.
[07:46 - 07:50] So this is essentially going to pre compute our assets. Great.
[07:51 - 08:01] And there are some other things you can define here such as a secret key and the Flask app programmatically. We've already defined those, so we don't really need to think about those.
[08:02 - 08:06] Okay. So the really only important one here is this asset build command.
[08:07 - 08:26] El Heroku to run a specific version of this by creating a one time .txt file. So if we go back in our code and look at the runtime.txt file, we can add Python 3.8.5 there for us because we haven't pushed up our proc file.
[08:27 - 08:41] So we're going to go and add those. And now that we've added them by running git status and running git commit AM as well git add dot, we can add the relevant files.
[08:42 - 08:46] One thing that's important is to not commit our public files. That's stored here.
[08:47 - 08:55] We want this to be built dynamically. So what we're going to do is add it to our git ignore as well as remove it from our git repository if it already exists.
[08:56 - 09:06] Now we're going to make a few changes to our config to make sure everything is publishable. So I'm going to define the sentry dsn and redis URL like so.
[09:07 - 09:12] And on the dev config, I'm going to set SQL on. I'm going to be database URI to SQLite slash dev.
[09:13 - 09:19] And that's the same idea for test config. But in our prod config, I want the database URI to come from the environment variable.
[09:20 - 09:31] Similarly, I'm going to fetch all the other ones that I think are important here to make sure that we can actually run redis in our database. Once I have this, I can have some confidence that our production code is ready to go.
[09:32 - 09:36] I'm going to add my changes to YumRoad. And then I'm going to go and connect them.
[09:37 - 09:47] It's really important that we also make sure that we're not accidentally committing the files in YumRoad's static public. We really don't want to be committing the built version of our files.
[09:48 - 09:55] We want Heroku to be able to be building the assets. All right, so we're going to commit this as config change.
[09:56 - 10:02] And then we're going to get push Heroku master. All right.
[10:03 - 10:14] So now we can see that it's deployed our application. Once our application is done deploying here, you can see that it's going to tell us that it's all deployed.
[10:15 - 10:22] Now we can switch into Firefox and check if our application is working. A quick way we can find out what's wrong is by checking Sentry.
[10:23 - 10:36] One thing we'll also need to do is install a module here to let our app connect to Postgres, which you'll be using in production. The way we're going to do that is to name the library reason.
[10:37 - 10:40] And then we're going to commit that as well. So going back in our terminal.
[10:41 - 10:47] Okay. So now we're going to look at our config and we're going to go and commit this change.
[10:48 - 11:01] Get push Heroku master. Okay.
[11:02 - 11:10] So now let's go and check our app. Now we're going to run Heroku run bash in order to connect to our production console here.
[11:11 - 11:16] What we're going to have to do is create our database. So there's two things we can do.
[11:17 - 11:27] We can either run our migrations by running flask and db migrate, or we can just reset the database. So I'm going to go ahead and reset the database here.
[11:28 - 11:31] So we're just going to follow the same commands we've followed before. So we're going to say from yumro.expensions.
[11:32 - 11:33] And then from yumro.models. Import everything.
[11:34 - 11:41] And then we're going to say db.growble. All right.
[11:42 - 11:47] And then we're going to say db.create call. Okay.
[11:48 - 11:53] So now we can go back in our browser and check out our application. Okay.
[11:54 - 12:01] So at this point we have an application and we can actually go ahead and register and sign up for product. Let's try it out.
[12:02 - 12:09] So I'm going to create my bookstore and I'm going to use my email here and I'm going to set up password. Okay.
[12:10 - 12:21] So I've set up a password and I count an email here. Now let's go ahead and check out our delayed jobs on dash rq.
[12:22 - 12:32] So you can see here that a delayed job has queued up, but there's no workers to process it. And the reason is we haven't told Heroku to actually run a worker node.
[12:33 - 12:56] So the way we're going to do that is back in our terminal, we're going to run Heroku PS scale and that's going to help us control how many of each type of server there is. So we're going to run PS scale web equals one and then we're going to do worker is equal to one because in our proc file we called it a worker.
[12:57 - 13:05] Let's go and run that. And then while that's running, I'm going to go back into our our our queue dashboard here and see what happens.
[13:06 - 13:11] Okay. So that went ahead and ran except it looks like it crashed.
[13:12 - 13:18] So our SMTP credentials are wrong. Let's go ahead and fix that and then we're going to rerun this.
[13:19 - 13:24] Okay. So I went and scaled down workers to zero, changed the environment variable and then set workers back up to one.
[13:25 - 13:31] Now for refresh the page, we'll eventually see this worker get registered in a moment. There it is.
[13:32 - 13:43] So what I can do on the failed job is I can go ahead and recue it and have it be processed and you can see that it's just was processed and I can see an email in my inbox now. So here it is.
[13:44 - 13:50] And that's sent from our production instance, which is a great. All right.
[13:51 - 14:06] Looking at our terminal now, we can see that the webhooks did in fact hit. So it's printing out the console and the text of the worker and their webhook right here, which is great.
[14:07 - 14:11] So I can now check my inbox to see if I actually did get an email. So that's Yumroad.
[14:12 - 14:17] Hopefully you've enjoyed building it this whole time and you can hopefully extend on it to build your own Sats applications as well.