Configuration & ENV_CONFIG
Lila uses a .env file as the single source of truth for all application settings.
Framework variables like DEBUG, PORT, and SECRET_KEY
are loaded automatically with proper type casting (string, integer, boolean).
For any custom variable you add to .env, the ENV_CONFIG proxy gives you instant access from anywhere in your project — no extra registration needed.
The .env File
After running lila-init, your project includes a .env file with all framework defaults.
To change any setting, just edit this file — no code changes are needed.
# ─── Server ────────────────────────────────────────────
# SECRET_KEY is mandatory: an empty/placeholder value makes Lila
# generate a persistent .lila_secret (0600) and warn.
SECRET_KEY="replace-with-a-long-random-string"
DEBUG=True
PORT=8000
HOST=127.0.0.1
WORKERS="1"
# ─── Application URL ──────────────────────────────────
# Production URL for sitemaps, robots.txt, canonical links and secure cookies.
# Leave empty in development — uses http://HOST:PORT automatically.
APP_URL=""
# ─── Project Metadata ─────────────────────────────────
TITLE_PROJECT="Lila Application"
VERSION_PROJECT="2.0.0"
DESCRIPTION_PROJECT="Full-stack lightweight Python application"
LANG_DEFAULT="es"
# ─── SEO Defaults ─────────────────────────────────────
DESCRIPTION_DEFAULT="Lila Framework 2.0 Web App"
KEYWORDS_DEFAULT="python, lila, spa, fast"
AUTHOR_DEFAULT="Lila Developer"
# ─── Cache ────────────────────────────────────────────
CACHE_DRIVER="memory" # memory | redis | file
CACHE_DIR="app/cache/data"
CACHE_MAX_ITEMS=10000
CACHE_DEFAULT_TTL=300
MODEL_CACHE_TTL=5
# ─── Your Custom Variables ─────────────────────────────
# Add any variable here and access it via ENV_CONFIG
STRIPE_API_KEY="sk_test_..."
Built-in Framework Variables
These variables are recognized by the framework automatically. They are type-casted (string → int, bool, etc.) and available as module-level constants via from app.config import ...
| Variable | Type | Default | Description |
|---|---|---|---|
| SECRET_KEY | str | "" |
Signs sessions and CSRF tokens. Empty/placeholder → generated once and stored in .lila_secret (0600). |
| PORT | int | 8000 |
Server port |
| HOST | str | "127.0.0.1" |
Server host address |
| APP_URL | str | "" |
Production URL (sitemaps, robots.txt, canonical links, secure cookies). Falls back to http://HOST:PORT when empty. |
| DEBUG | bool | False |
Debug mode (reload, dev SSE, /debug when debug_html=True). Scaffolds set it to True in development. |
| WORKERS | str | "1" |
Number of Uvicorn workers (used by the scaffolded main.py / lila run -w) |
| TITLE_PROJECT | str | "Lila Application" |
Project title (templates, OpenAPI, desktop window) |
| VERSION_PROJECT | str | "2.0" |
Project version (OpenAPI info) |
| DESCRIPTION_PROJECT | str | "Full-stack lightweight Python application" |
Project description for OpenAPI docs |
| LANG_DEFAULT | str | "es" |
Default language for i18n (lila init asks for it) |
| DESCRIPTION_DEFAULT | str | "Lila Framework 2.0 Web App" |
Default meta description for SEO |
| KEYWORDS_DEFAULT | str | "python, lila, spa, fast" |
Default meta keywords for SEO |
| AUTHOR_DEFAULT | str | "Lila Developer" |
Default author for meta tags |
| CACHE_DRIVER | str | "memory" |
memory, redis (only if reachable) or file |
| CACHE_DIR / CACHE_MAX_ITEMS / CACHE_DEFAULT_TTL | str / int / int | app/cache/data / 10000 / 300 |
File-cache directory, LRU capacity and default TTL |
| MODEL_CACHE_TTL | int | 5 |
Seconds a model read stays in the single-flight cache |
Using Framework Variables
There are two ways to read configuration:
from lila.core.config import ENV_CONFIG, is_debug— the framework proxy. Typed for schema keys, returns raw strings for custom keys.from app.config import DEBUG, HOST, PORT, WORKERS— the scaffold re-exports the loaded values as module constants (it callsConfigLoader.load()and updates its globals).
from app.config import DEBUG, HOST, PORT, SECRET_KEY
if DEBUG:
print(f"Running in debug mode on {HOST}:{PORT}")
# Or read typed values from the framework proxy
from lila.core.config import ENV_CONFIG, is_debug, get_secret_key
debug = is_debug() # bool, never bool("False")
max_items = ENV_CONFIG.get_int("CACHE_MAX_ITEMS", 10000)
origins = ENV_CONFIG.get_list("CORS_ORIGINS", [])
ENV_CONFIG — Access Any .env Variable
ENV_CONFIG is a dict-like proxy that gives you instant access to
any variable defined in your .env file — including custom ones
that are not part of the framework. No registration, no configuration — just add the variable to
.env and read it.
from app.config import ENV_CONFIG
# Access with [] — raises KeyError if not found
stripe_key = ENV_CONFIG["STRIPE_API_KEY"]
# Access with .get() — returns a default if not found
redis_url = ENV_CONFIG.get("REDIS_URL", "redis://localhost:6379")
# Check if a variable exists
if "CUSTOM_FEATURE_FLAG" in ENV_CONFIG:
print("Feature flag is set!")
# Works with any variable in .env — no registration needed
api_version = ENV_CONFIG.get("API_VERSION", "v1")
Real-World Example
A common pattern is combining framework variables with custom ENV_CONFIG variables
in your routes and services:
from lila.core.routing import Router
from lila.core.request import Request
from lila.core.responses import JSONResponse
from app.config import DEBUG, ENV_CONFIG
router = Router("api")
@router.get("/status")
async def status(request: Request):
return JSONResponse({
"debug": DEBUG,
"api_version": ENV_CONFIG.get("API_VERSION", "v1"),
"feature_x": ENV_CONFIG.get("ENABLE_FEATURE_X", "false"),
})
@router.post("/payment")
async def create_payment(request: Request):
# Access your Stripe key — defined only in .env
stripe_key = ENV_CONFIG["STRIPE_API_KEY"]
# ... payment logic
return JSONResponse({"success": True})
How app/config.py Works
After lila init, app/config.py
is clean and minimal. ConfigLoader loads .env, casts the framework
keys and exposes them; the scaffold copies those values into module globals:
import os
from os import path
from lila.core.config import ConfigLoader, ENV_CONFIG
# Loads .env + framework defaults once and exports them as constants.
_cfg = ConfigLoader.load()
globals().update(_cfg)
# Project paths (not read from .env)
PATH_LOG_BASE_DIR = "app/logs"
PATH_TEMPLATE_NOT_FOUND = "lila/404"
PATH_TEMPLATES_HTML = "resources/html/"
PATH_TEMPLATES_MARKDOWN = "resources/markdown/"
PATH_UPLOADS = path.join(os.getcwd(), "public", "uploads")
Framework variables are declared once in FRAMEWORK_SCHEMA inside
lila/core/config.py; ENV_CONFIG reads the loaded, typed values.
To change any value, edit .env and restart the server.
Loading & SECRET_KEY generation
ConfigLoader.load() runs once (it is idempotent) and caches the typed dict in
memory for the process. App() calls it automatically, so importing
lila.core.app directly still reads .env. There is no
config_cache.py file and no separate production cache step.
SECRET_KEY is special: an empty value, a value shorter than 16 characters or a
known placeholder makes get_secret_key() generate a strong key and persist it in
.lila_secret (mode 0600) with a warning. Lila never signs sessions or CSRF
tokens with an empty key. Rotate it with lila secret-key.
Performance Note
ENV_CONFIG reads values that were loaded at startup — it does not re-read .env on every access.
This means there is no per-request disk I/O when using ENV_CONFIG in your route handlers.
API Reference
| Method | Returns | Description |
|---|---|---|
| ENV_CONFIG["KEY"] | any | Get a value. Raises KeyError if not found. |
| ENV_CONFIG.get("KEY", default) | any | Get a value with a fallback default. Returns None if not found and no default given. |
| ENV_CONFIG.get_bool / get_int / get_list | bool / int / list | Typed helpers that never use bool("False"). |
| "KEY" in ENV_CONFIG | bool | Check if a variable exists in the configuration. |
| ConfigLoader.load() | dict | Loads .env + defaults once and returns the typed dict (idempotent). |
| ConfigLoader.reload() | dict | Clears the cached values and loads again (useful in tests). |
| get_secret_key() / is_debug() | str / bool | Fail-safe signing key and typed debug flag. |