Deploy Your Flask App to Render
This guide walks you through putting your Flask app on the internet so anyone can visit it. You will use Render, a free hosting service that connects to your GitHub repo. Every time you push new code from Codespaces, Render will automatically update your live site.
By the end you will have a real URL like https://your-app-name.onrender.com that you can share with anyone.
Before You Start
Make sure these are true:
- Your app runs in Codespaces when you type
uv run flask --app app run --debug(orpython app.py) and you can see it in the browser. - You have an
app.pyfile in the root of your project (not inside a folder). - You have a
templates/folder with your HTML files next toapp.py. - Your Flask variable is named
app— i.e.app = Flask(__name__)insideapp.py.
If your app does not run yet, fix that first. This guide is only for apps that already work.
Step 1 — Make Sure Dependencies Are in uv
Render can use the same uv setup you use in Codespaces. That means your dependencies should live in pyproject.toml and uv.lock.
- In the terminal, install
gunicornwithuv:
uv add gunicorn
gunicorn is a production server that Render uses to run your app (instead of the simple development server you use locally).
- If your app imports other packages, add them with
uv addtoo. Some common examples:
uv add python-dotenv
uv add resend
uv add requests
- Confirm that
pyproject.tomlanduv.lockare in the same folder asapp.py— not insidetemplates/or any other folder.
Step 2 — Create render.yaml
This file tells Render exactly how to build and start your app. Without it you would have to type all of this into the Render website by hand.
- Create another new file in the same root folder as
app.py. - Name it exactly:
render.yaml - Paste this in and save (change
your-app-nameto whatever you want the URL to be):
services:
- type: web
name: your-app-name
env: python
plan: free
buildCommand: uv sync --frozen && uv cache prune --ci
startCommand: uv run gunicorn app:app
autoDeploy: true
Here is what each line means:
| Line | What it does |
|---|---|
type: web | Tells Render this is a website |
name: your-app-name | The name that shows up on Render's dashboard |
env: python | Tells Render your app is Python |
plan: free | Uses the free tier (no credit card needed) |
buildCommand | Installs your packages from uv.lock |
startCommand | Starts your app using gunicorn through uv |
autoDeploy: true | Deploys automatically every time you push to GitHub |
If your app uses environment variables (API keys, secrets)
If your app loads anything from a .env file (API keys like RESEND_API_KEY, SMTP passwords, etc.), add an envVars block so Render knows they exist. You set the actual values in the Render dashboard — never in the repo.
services:
- type: web
name: your-app-name
env: python
plan: free
buildCommand: uv sync --frozen && uv cache prune --ci
startCommand: uv run gunicorn app:app
autoDeploy: true
envVars:
- key: RESEND_API_KEY
sync: false
- key: OWNER_EMAIL
sync: false
sync: false means "don't read this from the file, ask me in the dashboard." That's how secrets stay out of Git.
Step 3 — Push Your Changes to GitHub
Now you need to send these files to GitHub so Render can see them.
- Click the Source Control icon in the left sidebar (it looks like a branch/fork shape — the third icon from the top).
- You should see
render.yamllisted under Changes. You may also seepyproject.tomlanduv.lockifuv add gunicornchanged them. - Click the + button next to each changed file to stage it (this tells Git you want to include it).
- In the Message box at the top, type:
add render deploy files - Click the Commit button (the checkmark).
- Click Sync Changes to push your code up to GitHub.
If Codespaces asks you to confirm, click OK.
Go to your repo on GitHub and confirm that pyproject.toml, uv.lock, and render.yaml appear in the file list.
Step 4 — Create a Render Account
- Open a new browser tab and go to render.com.
- Click Get Started for Free (or Sign Up).
- Choose Sign up with GitHub. This is the easiest option because Render will already be connected to your code.
- GitHub will ask you to authorize Render. Click Authorize Render.
- You may need to grant Render access to the organization that owns your class repo. If you see a screen asking about organization access, make sure your class organization is checked, then click Grant or Approve.
You now have a Render account connected to your GitHub.
Step 5 — Create a Web Service on Render
- On the Render dashboard, click the New + button (top right area).
- Choose Web Service.
- Render will show a list of your GitHub repos. Find your final project repo and click Connect.
- If you do not see it, click Configure account and make sure Render has access to the organization that owns your repo.
- Render should auto-fill most settings from your
render.yaml. Double-check these:- Name:
your-app-name(or whatever you want your URL to be) - Environment:
Python - Build Command:
uv sync --frozen && uv cache prune --ci - Start Command:
uv run gunicorn app:app - Plan:
Free
- Name:
- If you added
envVarstorender.yaml, scroll down to the Environment Variables section and paste in the actual values (e.g. your realRESEND_API_KEY). These are stored on Render, not in Git. - Click Create Web Service.
Render will now start building your app. This takes a minute or two.
Step 6 — Watch the Deploy and Test Your App
- After clicking Create Web Service you will see a log of everything Render is doing. Look for messages like:
Running 'uv sync'...Build successfulYour service is live
- Once the deploy finishes, Render shows your live URL near the top of the page. It will look something like:
https://your-app-name.onrender.com - Click that link. You should see your app — the same one you see in Codespaces, but now on the real internet.
- Click through the core flow of your app (submit a form, click the main buttons, view any pages your demo depends on). Make sure everything works.
If something goes wrong, skip down to the Troubleshooting section below.
Step 7 — How Updates Work From Now On
Render is set to auto-deploy your app. That means the workflow going forward is simple:
- Edit your code in Codespaces like you normally do.
- Commit your changes (Source Control → type a message → click Commit).
- Sync Changes to push to GitHub.
- Render automatically picks up the change and redeploys your app. You do not need to go to the Render website.
Your live site updates in about 1–2 minutes after you push. You can watch the progress on your Render dashboard if you want, but you do not have to.
Edit code → Commit → Sync → Render auto-deploys → Live site updates
That's it. Every time you improve your app and push the code, the world sees the new version.
Important: SQLite on Render's Free Tier
If your app saves data to a SQLite file (e.g. data/leads.db, instance/app.db), know this:
Render's free tier has an ephemeral filesystem. Every time Render redeploys your app — including every push to GitHub — the disk is wiped and recreated. Any rows you saved through the live site will disappear on the next deploy.
For demo day, this is usually fine — you can submit a few fresh test rows right before your demo and they'll be there. But don't expect your live site to be a real data store.
The proper fix is to attach a Render Disk (paid) or switch to a hosted database like Render's Postgres free tier. See Render Disks docs if you need this.
Troubleshooting
"ModuleNotFoundError: No module named 'flask'" (or any other module)
Your pyproject.toml is missing a package, or uv.lock was not committed. In Codespaces, add the missing package with uv:
uv add package-name
For example: uv add resend, uv add python-dotenv, or uv add requests. Then commit pyproject.toml and uv.lock, push, and Render will rebuild.
"Application failed to respond" or the page won't load
This usually means the start command is wrong. Go to your Render dashboard → your service → Settings, and check that the Start Command is:
uv run gunicorn app:app
The first app is your filename (app.py). The second app is the Flask variable inside that file (app = Flask(__name__)). Both must match. If your file is named main.py and your Flask variable is still app, use uv run gunicorn main:app.
The app loads but looks unstyled (no colors, no layout)
If you use a CDN for your CSS framework (Bootstrap, Tailwind, etc.), make sure the <link> or <script> tag is still in your HTML <head>. For Bootstrap:
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
If you have a local static/style.css, make sure the static/ folder is committed to GitHub.
"TemplateNotFound" or "No such file or directory: templates/..."
Make sure your templates/ folder is committed to GitHub. In the Source Control panel in Codespaces, check that every file under templates/ has been pushed. If any show under Changes, stage and commit them.
"Internal Server Error" when using environment variables
Your code is calling os.environ["RESEND_API_KEY"] (or similar) and the variable isn't set on Render. Go to your service → Environment tab → add the key and its value → Save Changes. Render will redeploy.
Common mistake: you committed the key name in render.yaml with sync: false but forgot to paste the actual value into the dashboard.
Render says "Build failed" but you don't know why
Read the full build log on the Render dashboard. Look for red text or lines starting with ERROR. The most common cause is a missing dependency in pyproject.toml or a missing uv.lock. If your app imports something besides flask, add it with uv add package-name, then commit and push both pyproject.toml and uv.lock.
The free plan is slow to start
Render's free tier puts your app to sleep after 15 minutes of no traffic. The first visit after it sleeps takes 30–60 seconds to wake up. This is normal. After it wakes up, it stays fast until it goes idle again.
For demo day, open your site 1–2 minutes before you present so it's awake when the class sees it.
Quick Reference
| What | Value |
|---|---|
| Render dashboard | dashboard.render.com |
| Build command | uv sync --frozen && uv cache prune --ci |
| Start command | uv run gunicorn app:app |
| Live URL pattern | https://your-app-name.onrender.com |
| Free tier limits | 750 hours/month, sleeps after 15 min idle, ephemeral disk |
| Secrets | Set in dashboard → Environment tab. Never commit .env. Commit .env.example. |