Django Hello, World + Deployment

In this tutorial we will build a "Hello, World" website with Django and then deploy it to

If you want to learn Django properly, I provide step-by-step instructions and detailed explanations in my book Django for Beginners. The first four chapters are available free online.

Initial Set Up

Open a new command line shell. The code can live anywhere on your computer. We'll put it on the desktop in a folder called helloworld.

# Windows
$ cd onedrive\desktop\code
$ mkdir helloworld
$ cd helloworld

# macOS
$ cd ~/desktop/code
$ mkdir helloworld && cd helloworld

Create a new virtual environment called .venv, activate it, and install Django with Pip.

# Windows
$ python -m venv .venv
$ .venv\Scripts\Activate.ps1
(.venv) $ python -m pip install django~=4.1.0

# macOS
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ python3 -m pip install django~=4.1.0

Use the startproject comand to make a new Django project called django_project and a new app called pages.

(.venv) $ django-admin startproject django_project .
(.venv) $ python startapp pages 

Add the new pages app to the INSTALLED_APPS configuration.

# django_project/
    "pages.apps.PagesConfig",  # new

Then start up the local Django web server with the runserver command. And run the migrate command to remove warnings about "unapplied migrations."

(.venv) $ python runserver 
(.venv) $ python migrate

Open up in your web browser to see the Django welcome page.

Django Welcome Page

Django Hello, World

Now let's configure a basic view that returns the text "Hello, World!".

# pages/
from django.http import HttpResponse

def homePageView(request):
    return HttpResponse("Hello, World!")

Create a new file called pages/ with the following code.

# pages/
from django.urls import path

from .views import homePageView

urlpatterns = [
    path("", homePageView, name="home"),

And update the project-level django_project/ file as well.

# django_project/
from django.contrib import admin
from django.urls import path, include  # new

urlpatterns = [
    path("", include("pages.urls")),  # new

We're done! Start the local server again:

(.venv) $ python runserver

If you refresh the browser for it now displays the text "Hello, World!"

Hello, World in Local Browser

Django Deployment

Django is configured by default for local development. A proper production-ready deployment is quite involved but the following insecure steps will let us deploy our simple site for demonstration purposes.

Update ALLOWED_HOSTS to accept all hosts.

# django_project/ 

Install Gunicorn as our production server.

(.venv) $ python -m pip install gunicorn==20.1.0

Then create a requirements.txt file listing the packages in our Python virtual environment.

(.venv) $ pip freeze > requirements.txt

This creates a new requirements.txt file. If you look inside it there should be at least the following four packages:


Fly Deployment

Time for deployment. There are 6 steps required.

1) Install Fly's own command-line utility, flyctl.

# Windows
$ iwr -useb | iex

# macOS
$ brew install flyctl

2) Sign up or log in to

# Sign up for a new account
(.venv) $ fly auth signup

# Log in if existing account
(.venv) $ fly auth login has a generous free tier that is enough to try it out initially. You can only create 2 apps before being prompted for a payment method. Deployment employs real costs on hosting companies so it is a reasonable request. It also helps them crack down on fraud which is a major issue for all hosting companies with free tiers. Pricing is based on usage and quite reasonable especially compared to its equivalent on Heroku.

3) Create a new project-level file called Dockerfile which uses for deployments and add the following contents. This version is generously inspired by Jeff Triplett's open source examples.

ARG PYTHON_VERSION=3.10-slim-buster



RUN mkdir -p /app


COPY requirements.txt /tmp/requirements.txt

RUN set -ex && \
    pip install --upgrade pip && \
    pip install -r /tmp/requirements.txt && \
    rm -rf /root/.cache/

COPY . /app/


CMD ["gunicorn", "--bind", ":8000", "--workers", "2", "django_project.wsgi"]

Very briefly this installs Python 3.10, installs packages in the local requirements.txt file, exposes port 8000 (Django's default), and then uses Gunicorn as the production web server.

4) The fly launch command will detect this new Dockerfile and build it on Fly servers. There are four questions:

(.venv) $ fly launch
Creating app in /Users/wsv/Desktop/django-hello-world-fly
Scanning source code
Detected a Django app
? Overwrite "/Users/wsv/Desktop/django-hello-world-fly/Dockerfile"? No
? App Name (leave blank to use an auto-generated name): django-hello-world-fly
Automatically selected personal organization: Will Vincent
? Select region: iad (Ashburn, Virginia (US))
Created app django-hello-world-fly in organization personal
Set secrets on django-hello-world-fly: SECRET_KEY
Wrote config file fly.toml
? Would you like to set up a Postgresql database now? No
Your app is ready. Deploy with `flyctl deploy`

5) created a fly.toml file for us as part of the launch process. We need to update the two ports from 8080 to Django's preferred 8000 and also add a deploy section that will migrate the database automatically on each deploy. Here's what it looks like with ... indicating additional code.

  release_command = "python migrate --noinput"

  PORT = "8000"

  allowed_public_ports = []
  auto_rollback = true

  http_checks = []
  internal_port = 8000

6) That should be it. Now run fly deploy. Once it is done you can use fly open to automatically launch your website.

(.venv) $ flyctl deploy
(.venv) $ fly open

Mine is located at

Deployed Site

Conclusion is a very interesting hosting service that appears to be both more performant and cheaper than Heroku. At the moment, there is minimal Django support in the official documentation but that is likely to be changing soon.

In a future tutorial we'll look at how to deploy a Django app with a PostgreSQL database on

Join My Newsletter

Subscribe to get the latest tutorials/writings by email.

    No spam. Unsubscribe at any time.