Running Your App¶
Start, debug, and deploy your Aksara application.
Development Server¶
Using the CLI¶
The recommended way to run your app during development:
aksara dev defaults to main:app, enables auto-reload, and shows a rich startup banner with the app, admin, Studio, and docs URLs. aksara run dev is an equivalent alias if you prefer to stay on the run command family.
Representative output:
████╗ █████╗ ██╗ ██╗ ███████╗ █████╗ ██████╗ █████╗
████╔╝ ██╔══██╗ ██║ ██╔╝ ██╔════╝ ██╔══██╗ ██╔══██╗ ██╔══██╗
████╔╝ ██║ ██║ ██║ ██╔╝ ██║ ██║ ██║ ██║ ██║ ██║ ██║
████╔╝ ██║ ██║ ██║ ██╔╝ ██║ ██║ ██║ ██║ ██║ ██║ ██║
████████╗ ███████║ █████╔╝ ███████╗ ███████║ ██████╔╝ ███████║
╚══████╔╝ ██╔══██║ ██╔═██╗ ╚════██║ ██╔══██║ ██╔══██╗ ██╔══██║
████╔╝ ██║ ██║ ██║ ██╗ ██║ ██║ ██║ ██║ ██║ ██║ ██║
████╔╝ ██║ ██║ ██║ ██╗ ██║ ██║ ██║ ██║ ██║ ██║ ██║
████╔╝ ██║ ██║ ██║ ██╗ ███████║ ██║ ██║ ██║ ██║ ██║ ██║
╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
AI-native async backend · Dev Server · v0.5.46
● App http://127.0.0.1:8000/
● Admin http://127.0.0.1:8000/admin/
● Studio http://127.0.0.1:8000/studio/ui
● Docs http://127.0.0.1:8000/docs
Env dev · Reload enabled · Log info
Options:
| Option | Description | Default |
|---|---|---|
--reload / --no-reload |
Auto-reload on code changes | On |
--host |
Bind address | 127.0.0.1 |
--port |
Port number | 8000 |
--log-level |
Log level | info |
Examples:
# Development (auto-reload enabled by default)
aksara dev
# Alias for the same development workflow
aksara run dev
# Custom port
aksara dev --port 3000
# Bind to all interfaces
aksara dev --host 0.0.0.0 --port 8000
# Custom app path (if not main:app)
aksara dev myproject.main:app
Global Output Controls¶
All output-control flags are global, so they go before the command name:
--quiet suppresses non-error Aksara UI output such as the banner, progress lines, and success summaries. It does not suppress Uvicorn's own logs. --plain disables Rich rendering and animation. --no-color keeps the same content without ANSI color, and --force-color is useful when a supported terminal is not detected automatically.
Multi-worker testing
aksara dev always runs with a single worker. For production-like multi-worker testing, use aksara run main:app --workers 4.
Using Uvicorn Directly¶
You can also use Uvicorn directly:
Using Python¶
Interactive Shell¶
Access the interactive Python shell with your app context:
The shell automatically:
- Imports your models
- Connects to the database
- Provides an async-ready environment
Example session:
>>> # Models are auto-imported
>>> users = await User.objects.all()
>>> len(users)
5
>>> # Create new records
>>> task = await Task.objects.create(
... title="Review code",
... priority=2
... )
>>> task.id
UUID('...')
>>> # Query with filters
>>> pending = await Task.objects.filter(status="pending")
>>> for t in pending:
... print(f"{t.title}: {t.priority}")
Debug Mode¶
Enable debug mode for development:
Via Environment¶
Via Settings¶
Via Constructor¶
Debug mode enables:
- Detailed error pages with stack traces
- AI debugging suggestions for common errors
- Request/response logging
- Auto-reload (when using
--reload)
Never Use Debug Mode in Production
Debug mode exposes sensitive information. Always set AKSARA_DEBUG=false in production.
Production Deployment¶
Using Gunicorn + Uvicorn Workers¶
The recommended production setup:
gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--access-logfile - \
--error-logfile -
Using Docker¶
Create a Dockerfile:
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Run with Gunicorn
CMD ["gunicorn", "main:app", \
"--workers", "4", \
"--worker-class", "uvicorn.workers.UvicornWorker", \
"--bind", "0.0.0.0:8000"]
Create a docker-compose.yml:
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- AKSARA_DEBUG=false
- AKSARA_LOG_JSON=true
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Run with:
Worker Count¶
Calculate workers based on CPU cores:
| CPU Cores | Recommended Workers |
|---|---|
| 1 | 2-3 |
| 2 | 4-5 |
| 4 | 8-9 |
| 8 | 16-17 |
Environment Variables for Production¶
# Required
DATABASE_URL=postgresql://user:pass@host:5432/db?sslmode=require
# Recommended
AKSARA_DEBUG=false
AKSARA_LOG_LEVEL=INFO
AKSARA_LOG_JSON=true
AKSARA_POOL_MIN_SIZE=10
AKSARA_POOL_MAX_SIZE=50
Health Checks¶
Add health check endpoints for container orchestration:
from aksara.db import Database
@app.get("/health")
async def health():
"""Basic health check."""
return {"status": "ok"}
@app.get("/health/ready")
async def readiness():
"""Readiness check with database verification."""
try:
db = Database.get_instance()
await db.fetchval("SELECT 1")
return {"status": "ready", "database": "connected"}
except Exception as e:
return {"status": "not_ready", "error": str(e)}, 503
Process Management¶
Using systemd¶
Create /etc/systemd/system/aksara-app.service:
[Unit]
Description=Aksara Application
After=network.target postgresql.service
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
Environment="DATABASE_URL=postgresql://..."
Environment="AKSARA_DEBUG=false"
ExecStart=/opt/myapp/venv/bin/gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind unix:/run/aksara/app.sock
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and start:
Using Supervisor¶
Create /etc/supervisor/conf.d/aksara.conf:
[program:aksara]
command=/opt/myapp/venv/bin/gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
directory=/opt/myapp
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/aksara/app.log
environment=DATABASE_URL="postgresql://...",AKSARA_DEBUG="false"
Reverse Proxy¶
Nginx Configuration¶
upstream aksara {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://aksara;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
}
location /static {
alias /opt/myapp/static;
expires 30d;
}
}
Caddy Configuration¶
Logging¶
Development Logging¶
Production Logging¶
configure(
log_level="INFO",
log_requests=True,
log_json=True, # Machine-readable for log aggregation
)
Log Output¶
Development:
INFO: 127.0.0.1:52340 - "GET /api/tasks HTTP/1.1" 200
DEBUG: Query: SELECT * FROM tasks WHERE status = $1
Production (JSON):
{"timestamp":"2026-01-26T10:00:00Z","level":"INFO","method":"GET","path":"/api/tasks","status":200,"duration_ms":15,"request_id":"abc-123"}
Performance Tuning¶
Connection Pool¶
configure(
pool_min_size=10, # Keep connections ready
pool_max_size=50, # Limit maximum connections
)
Database Indexes¶
Ensure your frequently-queried fields are indexed:
class Task(Model):
status = fields.String(max_length=20, db_index=True)
created_at = fields.DateTime(auto_now_add=True, db_index=True)
Query Optimization¶
Use select_related for foreign keys:
# Efficient: single query with join
posts = await Post.objects.select_related("author").all()
# Inefficient: N+1 queries
posts = await Post.objects.all()
for post in posts:
author = await post.author # Separate query each time
Monitoring¶
Prometheus Metrics (Optional)¶
from prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app)
OpenTelemetry (Optional)¶
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(app)
Related Documentation¶
- Settings — Configuration options
- Middleware — Request processing
- Debugging — Runtime inspection and troubleshooting