<?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>DEV Community: Giovanni D'Amico</title>
    <description>The latest articles on DEV Community by Giovanni D'Amico (@giovannidevs).</description>
    <link>https://dev.to/giovannidevs</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%2F3862524%2Fa6837228-7a43-4d91-bbe5-d65a33246f82.png</url>
      <title>DEV Community: Giovanni D'Amico</title>
      <link>https://dev.to/giovannidevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/giovannidevs"/>
    <language>en</language>
    <item>
      <title>Deploying a Wagtail site on Render. A practical guide</title>
      <dc:creator>Giovanni D'Amico</dc:creator>
      <pubDate>Mon, 20 Apr 2026 15:44:54 +0000</pubDate>
      <link>https://dev.to/giovannidevs/deploying-a-wagtail-site-on-render-a-practical-guide-2f8c</link>
      <guid>https://dev.to/giovannidevs/deploying-a-wagtail-site-on-render-a-practical-guide-2f8c</guid>
      <description>&lt;h2&gt;
  
  
  Why I wrote this
&lt;/h2&gt;

&lt;p&gt;I recently finished the Wagtail tutorial and wanted to push my site to production on &lt;strong&gt;Render&lt;/strong&gt;. The official docs get you most of the way, but a handful of small issues cost me more time than I liked. This post is the guide I wish I'd had: a complete, working path from a fresh tutorial project to a live site, with &lt;strong&gt;Cloudinary&lt;/strong&gt; handling media uploads.&lt;/p&gt;

&lt;p&gt;I covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Wagtail site running on Render with Gunicorn&lt;/li&gt;
&lt;li&gt;A PostgreSQL database&lt;/li&gt;
&lt;li&gt;Static files served by WhiteNoise&lt;/li&gt;
&lt;li&gt;Cloudinary for images hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who is this for
&lt;/h2&gt;

&lt;p&gt;If you’ve just finished (or are partway through) the Wagtail tutorial and you’re ready to get your site live, this guide is for you. It’s written for early‑stage developers who want a practical, end‑to‑end deployment path using &lt;strong&gt;Render&lt;/strong&gt; and &lt;strong&gt;Cloudinary&lt;/strong&gt;, not an advanced deep dive.&lt;/p&gt;

&lt;p&gt;We’ll cover the basic but essential pieces that tend to trip people up (production settings, environment variables, static files, media storage, database setup, and common errors), hoping this can help you in the process and eventual debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Cloudinary: Render's filesystem is ephemeral
&lt;/h2&gt;

&lt;p&gt;Render's web services wipe the disk on every deploy or restart. Any images you upload through Wagtail's admin would vanish the next time the app rebuilds. &lt;strong&gt;Cloudinary&lt;/strong&gt; fixes this by storing media files in the cloud and serving them via CDN.&lt;/p&gt;

&lt;p&gt;You don't change any models, templates, or blocks. &lt;code&gt;ForeignKey('wagtailimages.Image', ...)&lt;/code&gt;, &lt;code&gt;ImageBlock()&lt;/code&gt;, &lt;code&gt;FieldPanel('image')&lt;/code&gt;, and &lt;code&gt;{% image %}&lt;/code&gt; all stay exactly the same. Cloudinary plugs in at the storage layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point
&lt;/h2&gt;

&lt;p&gt;This guide assumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You've finished the Wagtail tutorial (or have a Wagtail site to deploy)&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;.gitignore&lt;/code&gt; is set up&lt;/li&gt;
&lt;li&gt;You haven't touched deployment yet&lt;/li&gt;
&lt;li&gt;I won't cover the database data transfer. I simply recreated the pages after deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note on the project structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The issue I had and what you should try to avoid
&lt;/h3&gt;

&lt;p&gt;This was my file structure ready for deployment, and my Git (and GitHub) repo ROOT was not the same as &lt;a href="http://manage.py" rel="noopener noreferrer"&gt;manage.py&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysite/
├── .venv/
├── mysite/
│   ├── base/ blog/ home/ portfolio/ search/
│   ├── media/
│   ├── mysite/
│   │   ├── settings/
│   │   │   ├── base.py ← shared settings
│   │   │   ├── dev.py ← local dev
│   │   │   └── production.py ← we'll rewrite this
│   │   ├── static/ templates/
│   │   ├── urls.py wsgi.py
│   ├── db.sqlite3 manage.py requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;If your Git repo root is NOT the same folder as &lt;code&gt;manage.py&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This happens when you run &lt;code&gt;git init&lt;/code&gt; in a parent folder (e.g. &lt;code&gt;personal/&lt;/code&gt;) while your Wagtail project lives in a subfolder (e.g. &lt;code&gt;personal/mysite/&lt;/code&gt;). You can check by running &lt;code&gt;git rev-parse --show-toplevel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If it doesn't point to the folder containing &lt;code&gt;manage.py&lt;/code&gt;, you have this issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1 (cleanest — no code changes needed):&lt;/strong&gt; On Render, when creating your Web Service, set the &lt;strong&gt;Root Directory&lt;/strong&gt; field to &lt;code&gt;mysite&lt;/code&gt; (or whatever your subfolder is called). This tells Render to run all commands from that directory instead of the repo root. Your &lt;code&gt;build.sh&lt;/code&gt; and start command stay exactly as written — no &lt;code&gt;cd&lt;/code&gt; needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2 (if you can't set Root Directory):&lt;/strong&gt; Add &lt;code&gt;cd mysite&lt;/code&gt; to the top of &lt;code&gt;build.sh&lt;/code&gt;:&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;-o&lt;/span&gt; errexit
&lt;span class="nb"&gt;cd &lt;/span&gt;mysite
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python manage.py collectstatic &lt;span class="nt"&gt;--no-input&lt;/span&gt; &lt;span class="nt"&gt;--clear&lt;/span&gt;
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And on Render, set the Start Command to:&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="nb"&gt;cd &lt;/span&gt;mysite &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; gunicorn mysite.wsgi:application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid this in future projects, always run &lt;code&gt;wagtail start mysite .&lt;/code&gt; (note the &lt;code&gt;.&lt;/code&gt; at the end) so project files are created in the current directory, and then run &lt;code&gt;git init&lt;/code&gt; in that same directory — so the repo root and &lt;a href="http://manage.py" rel="noopener noreferrer"&gt;manage.py&lt;/a&gt; are in the same place.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase A — Prepare your project locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install production dependencies
&lt;/h3&gt;

&lt;p&gt;Activate your virtual environment, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;gunicorn psycopg2-binary dj-database-url whitenoise django-cloudinary-storage cloudinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What each one does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;gunicorn&lt;/strong&gt; — production WSGI server (replaces &lt;code&gt;runserver&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;psycopg2-binary&lt;/strong&gt; — PostgreSQL driver for Django.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dj-database-url&lt;/strong&gt; — turns Render's &lt;code&gt;DATABASE_URL&lt;/code&gt; string into Django's &lt;code&gt;DATABASES&lt;/code&gt; dict.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;whitenoise&lt;/strong&gt; — serves static files (CSS/JS) in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;django-cloudinary-storage&lt;/strong&gt; — redirects file uploads to Cloudinary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cloudinary&lt;/strong&gt; — the underlying SDK.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lock them in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a Cloudinary account
&lt;/h3&gt;

&lt;p&gt;Sign up at &lt;a href="http://cloudinary.com/" rel="noopener noreferrer"&gt;http://cloudinary.com/&lt;/a&gt; (free tier: 25 GB storage, 25 GB bandwidth/month — plenty for a portfolio). From the dashboard, grab:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cloud Name&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Key&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Secret&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep them safe, they'll become environment variables on Render. Never commit them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Generate a production &lt;code&gt;SECRET_KEY&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Your &lt;code&gt;dev.py&lt;/code&gt; has a hardcoded key that's fine for local work. Production needs its own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output somewhere safe. Don't put it in any file in the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Update &lt;code&gt;base.py&lt;/code&gt; — apps and middleware
&lt;/h3&gt;

&lt;p&gt;Add Cloudinary to &lt;code&gt;INSTALLED_APPS&lt;/code&gt;. &lt;strong&gt;Order matters:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ... your apps ...
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.staticfiles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudinary_storage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# AFTER staticfiles
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudinary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;# AFTER cloudinary_storage
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.postgres&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# required for Wagtail search on PostgreSQL
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;code&gt;cloudinary_storage&lt;/code&gt; &lt;strong&gt;must&lt;/strong&gt; come after &lt;code&gt;django.contrib.staticfiles&lt;/code&gt;. Otherwise it hijacks &lt;code&gt;collectstatic&lt;/code&gt; with its own version and silently collects zero files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then add WhiteNoise middleware — directly after &lt;code&gt;SecurityMiddleware&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.middleware.security.SecurityMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whitenoise.middleware.WhiteNoiseMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# ← here
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.sessions.middleware.SessionMiddleware&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WhiteNoise does nothing when &lt;code&gt;DEBUG=True&lt;/code&gt;, so this is safe for local dev too.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Configure &lt;code&gt;production.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is what Render is going to use during deployment. Some key aspects about how it is set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;os.environ["SECRET_KEY"]&lt;/code&gt; (not &lt;code&gt;.get&lt;/code&gt;) means the app crashes immediately if you forget to set it — you'll know at once.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;STORAGES&lt;/code&gt; has two independent keys: &lt;code&gt;"default"&lt;/code&gt; handles media uploads (Cloudinary); &lt;code&gt;"staticfiles"&lt;/code&gt; handles CSS/JS (WhiteNoise).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;STATICFILES_STORAGE = ...&lt;/code&gt; line is a workaround for &lt;code&gt;django-cloudinary-storage&lt;/code&gt; 0.3.0, which still references a setting Django 6 removed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dj_database_url&lt;/span&gt;

&lt;span class="c1"&gt;# ===== SECURITY =====
&lt;/span&gt;&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;ALLOWED_HOSTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALLOWED_HOSTS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SECURE_PROXY_SSL_HEADER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP_X_FORWARDED_PROTO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SECURE_SSL_REDIRECT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;CSRF_TRUSTED_ORIGINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DJANGO_CSRF_TRUSTED_ORIGINS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ===== DATABASE =====
&lt;/span&gt;&lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dj_database_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn_max_age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# ===== MEDIA (Cloudinary) &amp;amp; STATIC (WhiteNoise) =====
&lt;/span&gt;&lt;span class="n"&gt;CLOUDINARY_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUD_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;STORAGES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BACKEND&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudinary_storage.storage.MediaCloudinaryStorage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staticfiles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BACKEND&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whitenoise.storage.CompressedManifestStaticFilesStorage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Shim for django-cloudinary-storage 0.3.0 on Django 6+
&lt;/span&gt;&lt;span class="n"&gt;STATICFILES_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STORAGES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staticfiles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BACKEND&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# ===== WAGTAIL =====
&lt;/span&gt;&lt;span class="n"&gt;WAGTAIL_REDIRECTS_FILE_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# ===== LOCAL OVERRIDES =====
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.local&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Add a &lt;code&gt;build.sh&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Render needs to know what to do when it builds your app. Create a new file called &lt;code&gt;build.sh&lt;/code&gt; in your project root (same level as &lt;code&gt;manage.py&lt;/code&gt;):&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;-o&lt;/span&gt; errexit

pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python manage.py collectstatic &lt;span class="nt"&gt;--no-input&lt;/span&gt; &lt;span class="nt"&gt;--clear&lt;/span&gt;
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable:&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="nb"&gt;chmod&lt;/span&gt; +x build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, &lt;code&gt;chmod&lt;/code&gt; won't work — use Git instead:&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 build.sh
git update-index &lt;span class="nt"&gt;--chmod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;+x build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Check, commit and push
&lt;/h3&gt;

&lt;p&gt;Sanity-check: &lt;code&gt;.venv/&lt;/code&gt;, &lt;code&gt;db.sqlite3&lt;/code&gt;, &lt;code&gt;__pycache__/&lt;/code&gt;, and &lt;code&gt;media/&lt;/code&gt; should NOT be in the commit. &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;build.sh&lt;/code&gt;, and the updated settings files SHOULD be.&lt;/p&gt;

&lt;p&gt;Then commit and push.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase B — Set up Render
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. Create a PostgreSQL database
&lt;/h3&gt;

&lt;p&gt;Render dashboard → &lt;strong&gt;New → PostgreSQL&lt;/strong&gt;. Pick a region close to you, leave the defaults, choose the &lt;strong&gt;Free&lt;/strong&gt; tier.&lt;/p&gt;

&lt;p&gt;Once it's created, copy the &lt;strong&gt;Internal Database URL&lt;/strong&gt; from the Connections section. Save it for later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Free databases expire after 30 days. Upgrade to Basic ($6/mo) if you want it to be persistent.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Wagtail's starter template ships a &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;.dockerignore&lt;/code&gt;. Remove them first or Render will default to Docker and hide the Build/Start command fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nb"&gt;rm &lt;/span&gt;mysite/Dockerfile mysite/.dockerignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dashboard → &lt;strong&gt;New → Web Service&lt;/strong&gt; → connect your GitHub repo. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Command:&lt;/strong&gt; &lt;code&gt;./build.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start Command:&lt;/strong&gt; &lt;code&gt;gunicorn mysite.wsgi:application&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instance type:&lt;/strong&gt; Free (spins down after 15 min) or Starter ($7/mo, always on)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. Environment variables
&lt;/h3&gt;

&lt;p&gt;On Render, find the Environment variables settings and set &lt;strong&gt;all&lt;/strong&gt; of these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;SECRET_KEY&lt;/code&gt; — The key you generated in step 3&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DATABASE_URL&lt;/code&gt; — Internal Database URL from step 8&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DJANGO_SETTINGS_MODULE&lt;/code&gt; — &lt;code&gt;mysite.settings.production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; — &lt;code&gt;your-name.onrender.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DJANGO_CSRF_TRUSTED_ORIGINS&lt;/code&gt; — &lt;code&gt;https://your-name.onrender.com&lt;/code&gt; (must include scheme)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PYTHON_VERSION&lt;/code&gt; — Match your local version (e.g. &lt;code&gt;3.12.3&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CLOUDINARY_CLOUD_NAME&lt;/code&gt; — From Cloudinary dashboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CLOUDINARY_API_KEY&lt;/code&gt; — From Cloudinary dashboard&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CLOUDINARY_API_SECRET&lt;/code&gt; — From Cloudinary dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  11. Deploy
&lt;/h3&gt;

&lt;p&gt;Click &lt;strong&gt;Create Web Service&lt;/strong&gt; (or &lt;strong&gt;Manual Deploy → Deploy latest commit&lt;/strong&gt;). Watch the logs for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Installing requirements&lt;/code&gt; — pip ran&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;static files copied&lt;/code&gt; — &lt;code&gt;collectstatic&lt;/code&gt; worked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Running migrations&lt;/code&gt; — PostgreSQL tables created&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Listening on 0.0.0.0:...&lt;/code&gt; — Gunicorn is up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  12. Create a superuser
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Shell tab is not available on the Free plan&lt;/strong&gt;. Use the environment variable method below instead.&lt;/p&gt;

&lt;p&gt;If you’re on a paid plan, you can use the Shell tab and run &lt;code&gt;python manage.py createsuperuser&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;Otherwise, on a free plan, use the env-var approach:&lt;/p&gt;

&lt;p&gt;Add three variables on Render:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DJANGO_SUPERUSER_USERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DJANGO_SUPERUSER_EMAIL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DJANGO_SUPERUSER_PASSWORD&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then append this line to &lt;code&gt;build.sh&lt;/code&gt; (after &lt;code&gt;migrate&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python manage.py createsuperuser &lt;span class="nt"&gt;--no-input&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit, push, redeploy. Log in at &lt;code&gt;/admin/&lt;/code&gt;. Afterwards, remove the line and the three env vars. Just take note of them somewhere safe. No, not on the back of your latest gas bill.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Add content
&lt;/h3&gt;

&lt;p&gt;Your local SQLite data isn't transferred. Just re-create your pages in the Wagtail admin. Images now go straight to Cloudinary.&lt;/p&gt;

&lt;p&gt;In this guide, I'm not covering data transferring, but you can look into it if you have lots of content. Images will still need to be reuploaded.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase C — Verify
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Homepage loads at &lt;code&gt;https://your-name.onrender.com/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSS and styling look right (→ WhiteNoise is working)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/admin/&lt;/code&gt; login works&lt;/li&gt;
&lt;li&gt;Upload a test image → it shows on the frontend&lt;/li&gt;
&lt;li&gt;The image appears in your Cloudinary Media Library&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Debugging and Issues I hit (and fixes)
&lt;/h2&gt;

&lt;p&gt;A log of everything that went wrong and how I solved it. If you're debugging, Ctrl+F here first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - &lt;code&gt;AttributeError: 'Settings' object has no attribute 'STATICFILES_STORAGE'&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;django-cloudinary-storage&lt;/code&gt; 0.3.0 still references a setting Django 6 removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add &lt;code&gt;STATICFILES_STORAGE = STORAGES["staticfiles"]["BACKEND"]&lt;/code&gt; after the &lt;code&gt;STORAGES&lt;/code&gt; block.&lt;/p&gt;

&lt;h3&gt;
  
  
  2 - &lt;code&gt;SystemCheckError: 'django.contrib.postgres' must be in INSTALLED_APPS&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Wagtail search uses &lt;code&gt;SearchVectorField&lt;/code&gt;, which needs this app when PostgreSQL is the backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add &lt;code&gt;"django.contrib.postgres"&lt;/code&gt; to &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in &lt;code&gt;base.py&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3 - &lt;code&gt;NodeNotFoundError&lt;/code&gt; during migrate
&lt;/h3&gt;

&lt;p&gt;If you mix Wagtail versions (e.g. generate migrations on 7.3rc1, then downgrade to 7.2), migration parents break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; stick to one version across dev and production.&lt;/p&gt;

&lt;h3&gt;
  
  
  4 - &lt;code&gt;Bad Request (400)&lt;/code&gt; on the live site
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; doesn't match the actual domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; set it to exactly &lt;code&gt;your-name.onrender.com&lt;/code&gt;, no scheme, no trailing spaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  5 - &lt;code&gt;CSRF_TRUSTED_ORIGINS&lt;/code&gt; build error
&lt;/h3&gt;

&lt;p&gt;Env var missing → &lt;code&gt;.split(",")&lt;/code&gt; returns &lt;code&gt;[""]&lt;/code&gt; → Django rejects it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; set &lt;code&gt;DJANGO_CSRF_TRUSTED_ORIGINS&lt;/code&gt; to &lt;code&gt;https://your-name.onrender.com&lt;/code&gt; (with the scheme).&lt;/p&gt;

&lt;h3&gt;
  
  
  6 - &lt;code&gt;0 static files copied&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cloudinary_storage&lt;/code&gt; was listed &lt;strong&gt;before&lt;/strong&gt; &lt;code&gt;django.contrib.staticfiles&lt;/code&gt;, so its custom &lt;code&gt;collectstatic&lt;/code&gt; silently did nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; reorder — &lt;code&gt;cloudinary_storage&lt;/code&gt; comes after &lt;code&gt;staticfiles&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  7 - Render defaulting to Docker
&lt;/h3&gt;

&lt;p&gt;It auto-detected the Wagtail starter's &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;code&gt;git rm mysite/Dockerfile mysite/.dockerignore&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  8 - &lt;code&gt;build.sh&lt;/code&gt; can't find &lt;code&gt;manage.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Git repo root was one level above the Wagtail project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; set Render's &lt;strong&gt;Root Directory&lt;/strong&gt;, or add &lt;code&gt;cd mysite&lt;/code&gt; to the build and start commands.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up and personal considerations
&lt;/h2&gt;

&lt;p&gt;A quick note before the victory lap: this is the procedure that worked &lt;strong&gt;for me&lt;/strong&gt;. It's not necessarily the foolproof, one-size-fits-all path. I got here by researching, experimenting, and leaning on AI to reason through my specific scenario (a fresh tutorial project, Django 6, Wagtail 7, Render + Cloudinary). Your stack versions or project layout might nudge you off this exact route, and that's fine.&lt;/p&gt;

&lt;p&gt;That said, if this guide worked for you, you should now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Wagtail site live on Render&lt;/li&gt;
&lt;li&gt;Persistent PostgreSQL&lt;/li&gt;
&lt;li&gt;Media uploads going to Cloudinary.&lt;/li&gt;
&lt;li&gt;Static files properly served in production&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where I went from here
&lt;/h2&gt;

&lt;p&gt;Once everything was working end-to-end, I made a few follow-up moves to turn this from a free experiment into something I actually want to keep online:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upgraded the Render web service to a paid plan&lt;/strong&gt; so it stops spinning down after 15 minutes of inactivity, no more cold-start delay when someone visits, maybe overkill for a portfolio, but I can always downgrade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bought a custom domain through Cloudflare&lt;/strong&gt; and pointed it at the Render service, which also gave me their DNS and SSL setup for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moving the PostgreSQL database to a paid Render plan&lt;/strong&gt; before the 30-day free expiry, so I don't lose the database (and have to redo migrations and content) just as things settle in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is required to &lt;em&gt;get&lt;/em&gt; the site live. The free tier is actually enough to prove the whole pipeline works, but they're the natural next steps once you want the site to stick around.&lt;/p&gt;

&lt;p&gt;If this helped, or if you hit a fresh issue I haven't covered, I'd love to hear about it. Mostly, I hope this proves useful to someone approaching the same case as mine: a first Wagtail project, freshly out of the tutorial, trying to get it deployed on Render. Happy deploying.&lt;/p&gt;

</description>
      <category>wagtail</category>
      <category>render</category>
      <category>django</category>
      <category>deploy</category>
    </item>
  </channel>
</rss>
