Skip to content

Flask Directory Structure

Flask lets you start with a single file, but real apps need structure.

A clean structure makes it easier to:

  • scale your codebase
  • write tests
  • add Blueprints
  • deploy reliably

Option 1: single-file starter (learning only)

Section titled “Option 1: single-file starter (learning only)”
text
app.py

This is okay for “Hello World”, but it gets messy fast.

Section titled “Option 2: recommended starter package layout”
text
myapp/
  app.py
  templates/
    home.html
  static/
    styles.css

Flask will auto-discover templates/ and static/ relative to your app.

Option 3: scalable layout (closer to production)

Section titled “Option 3: scalable layout (closer to production)”
text
myapp/
  __init__.py          # create_app() lives here
  routes.py            # routes / views
  models.py            # database models
  templates/
  static/
config.py
wsgi.py                # entrypoint for gunicorn

A typical production command is:

  • gunicorn "wsgi:app"

So wsgi.py is an easy “server entry point”.

In Phase 2 you’ll learn routing and views in depth.

In Phase 7 you’ll learn:

  • Blueprints
  • application factory pattern

Those concepts make Option 3 feel natural.

The paths Flask assumes before you configure anything

Section titled “The paths Flask assumes before you configure anything”

Flask(__name__) derives every location from where that module lives. Measured on a fresh app:

diagram Diagram mermaid
attributemeasured value
app.root_paththe directory of the module that called Flask()
app.static_folder<root_path>/static — an absolute path
app.static_url_path/static
app.template_foldertemplates — a relative path
app.instance_path<root_path>/instance

Note the inconsistency, because it causes real confusion: static_folder comes back absolute and template_folder comes back relative. Both are resolved against root_path; only their representation differs.

the layout those defaults expect
myapp/
  app.py            <- root_path is this folder
  static/           <- served at /static, no code needed
    style.css
  templates/        <- render_template looks here
    index.html
  instance/         <- config and sqlite that must NOT be in git
    config.py

A single app.py is correct until routes multiply or you need more than one app instance (say, one for tests). The next step is a package with an application factory:

the factory layout
myapp/
  __init__.py       <- create_app() lives here
  routes/
    __init__.py
    users.py        <- a Blueprint
  models.py
  templates/
  static/
tests/
config.py
__init__.py
from flask import Flask
 
def create_app(config=None):
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_object(config or "config.Default")
 
    from .routes.users import bp as users_bp
    app.register_blueprint(users_bp, url_prefix="/users")
    return app

The factory matters because a module-level app = Flask(__name__) is created at import time with one fixed configuration. A factory lets tests build an app with a temporary database and throw it away, which is the difference between testable and not.

sketch Where Flask looks for each kind of file p5.js
Every default path is derived from root_path, which is the folder holding the module that called Flask().
pch.quizTag pch.quizDefaultTitle
  1. What determines app.root_path?

    pch.quizShowAnswer

    B — the folder containing the module that called Flask(__name__) — Flask derives root_path from the module you pass as __name__, and static_folder, template_folder and instance_path are all resolved from it. That is why moving app.py moves everything.

  2. What is the instance/ folder for?

    pch.quizShowAnswer

    B — configuration and data files such as the SQLite database that must not be committed — instance_path sits outside the package, is created on demand, and is the intended home for secrets and the database. It belongs in .gitignore.

  3. Why prefer an application factory over a module-level app = Flask(__name__)?

    pch.quizShowAnswer

    C — it lets you build an app per configuration, so tests can create one with a temporary database and discard it — A module-level app is created once at import with one fixed configuration. A factory makes configuration a parameter, which is what makes the app testable.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading