# 15 | Production build and Render

## Goal

Build optimized assets and deploy one persistent application instance.

## What you are building

The Blueprint installs pinned dependencies and builds Teloce with MinifyJS. The start command applies committed Python migrations, then starts one Uvicorn worker. SQLite and staff/CMS data live under /var/data on a paid persistent disk. Render's build and pre-deploy processes cannot access that disk, which is why this design migrates at startup. Set PUBLIC_ORIGIN to your exact HTTPS service URL, DEBUG to false and a persistent generated secret. This course deliberately uses one instance: scaling needs a shared database, sessions, media and CMS coordination strategy.

## Build this chapter

Stop the server. Work in project_manager. Follow each CREATE, whole-file replacement, or marked-block edit below in order.

## Step 1: CREATE - make parent folders, then create this file - .env.example

This file owns the feature named by its module or component. Read the route/form flow before continuing.

Create the parent directories, then save this complete file.

```text
FLAXON_DEBUG=1
PUBLIC_ORIGIN=http://127.0.0.1:8000
# Production: FLAXON_DEBUG=0, PUBLIC_ORIGIN=https://your-app.onrender.com
# Render persistent disk mount: DATA_DIR=/var/data
# Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))"
FLAXON_SECRET_KEY=
```

**Say:** "This file owns the feature named by its module or component. Read the route/form flow before continuing."

**Type:** Follow the action above in `.env.example`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Step 2: CREATE - make parent folders, then create this file - scripts/build_ui.py

This file owns the feature named by its module or component. Read the route/form flow before continuing.

Create the parent directories, then save this complete file.

```python
"""Build production Teloce assets without touching the live database or Render disk."""

import os
import secrets
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
with tempfile.TemporaryDirectory() as build_data:
    os.environ["DATA_DIR"] = build_data
    os.environ["FLAXON_DEBUG"] = "0"
    os.environ.setdefault("FLAXON_SECRET_KEY", secrets.token_urlsafe(48))
    from app import app

    result = app.teloce.build()
    print(f"Built {result.get('compiled', 0)} Teloce components with MinifyJS.")
```

**Say:** "This file owns the feature named by its module or component. Read the route/form flow before continuing."

**Type:** Follow the action above in `scripts/build_ui.py`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Step 3: CREATE - make parent folders, then create this file - render.yaml

This is the complete single-instance deployment definition. Supply the service-specific HTTPS origin in Render.

Create the parent directories, then save this complete file.

```yaml
services:
  - type: web
    name: flaxon-project-manager
    runtime: python
    plan: starter
    buildCommand: pip install -r requirements.txt && python scripts/build_ui.py
    startCommand: python management.py migrate && python -m uvicorn app:app --host 0.0.0.0 --port $PORT --workers 1
    healthCheckPath: /health/course
    disk:
      name: project-manager-data
      mountPath: /var/data
      sizeGB: 1
    envVars:
      - key: PYTHON_VERSION
        value: "3.12.14"
      - key: FLAXON_DEBUG
        value: "0"
      - key: FLAXON_SECRET_KEY
        generateValue: true
      - key: DATA_DIR
        value: /var/data
      - key: PUBLIC_ORIGIN
        sync: false
```

**Say:** "This is the complete single-instance deployment definition. Supply the service-specific HTTPS origin in Render."

**Type:** Follow the action above in `render.yaml`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Run and check

```bash
python scripts/build_ui.py
python -m pip check
```

## Deploy the completed project

1. Stop the local server. Run all tests and both browser checks from chapter 14. Run the production build above.
2. Commit your Python migrations, application files and requirements. Keep .env, data, cookie jars and local virtual environments out of Git. Push the project to your GitHub repository.
3. In Render, choose New > Blueprint, connect your repository and select the branch containing render.yaml. Review the paid service and persistent disk before deploying.
4. Supply PUBLIC_ORIGIN as the exact https://SERVICE.onrender.com address (or your configured HTTPS domain). Keep FLAXON_DEBUG=0, DATA_DIR=/var/data and the generated persistent secret. Python is pinned by the Blueprint.
5. Deploy and inspect logs: migrations must complete before Uvicorn listens. Do not move SQLite migrations to a pre-deploy command; that process cannot access the attached disk.
6. In the service Shell run python management.py setup-admin and enter staff credentials interactively. Never commit a staff password.
7. Visit /health/course, then register a customer, create a project and task, sign into staff Admin and publish a help article. Refresh /projects/ID and /help/SLUG. Sign out and check protected API requests are rejected.
8. Restart the service and confirm both the project and help article survive. Inspect browser cookies over HTTPS for Secure and HttpOnly. Back up data and uploads before upgrades.

Official deployment references, checked October 2026: [Render Blueprints](https://render.com/docs/blueprint-spec), [persistent disks](https://render.com/docs/disks), [deployment lifecycle](https://render.com/docs/deploys), and [Python versions](https://render.com/docs/python-version). This book supplies deployable configuration; it does not claim a deployment has been made in your account.

## Expected result

After deployment, /health/course returns ok. Create a project/task, publish an article, refresh a nested route, sign out and restart the service. Customer and staff data survive; cookies use Secure and HttpOnly.

## Common errors

Free Render services cannot attach this persistent disk. Do not generate new migration files during deployment: commit them beforehand. An incorrect PUBLIC_ORIGIN causes CSRF failures. An ephemeral database loses records on restart. Production deployment must be verified in your account; local smoke is not proof of a live deployment.

## Short exercise

Back up both SQLite files safely, restore them into a test environment and verify a project plus one published help article.

## What to say and show

Say: "Build optimized assets and deploy one persistent application instance. The server remains responsible for persistence and authorization; the browser presents the result."

Show the expected result above, then point to the file responsible for it. Run the chapter check before the next lesson.

## Save your checkpoint

```bash
git add .
git commit -m "Complete chapter 15: Production build and Render"
```
