Set up PostgreSQL¶
Aksara uses PostgreSQL through asyncpg. For the tested versions, see runtime compatibility. Install the server using the PostgreSQL downloads and platform instructions, or use a database supplied by your development environment. Installing the Python package does not provision PostgreSQL.
Use a database reserved for the tutorial or application. Migrations change its schema; do not point a new tutorial at unrelated application data.
Configure a local project¶
From the generated project directory, the interactive helper can write the connection URL:
It checks reachability, asks for a database name, username and password, connects
to the maintenance database postgres, and creates the requested database if
it is absent. An existing database is retained. The role needs access to the
maintenance database and, for a new database, permission to create it. The
helper does not create application roles or configure production privileges.
--host and --port choose another server. If .env already contains
DATABASE_URL, the helper asks before overwriting it; an existing URL can
supply the host and port. Check the selected target rather than assuming that
an overwrite switches servers.
You can instead edit .env with credentials supplied by your database
administrator. This is a format example, not a working credential:
URL-encode special characters in credentials. Keep secrets out of source,
logs and support reports. AKSARA_DATABASE_URL takes precedence over
DATABASE_URL; use one consistently so an old value cannot redirect commands.
Existing process environment takes precedence over values loaded from .env.
A file named .env.test or .env.production is not selected automatically.
See settings precedence.
Verify a connection without changing tables¶
Save this standalone probe as check_database.py in the project directory:
import asyncio
from aksara.conf import settings
from aksara.db import Database
async def check_database():
if not settings.database_url:
raise RuntimeError("Set AKSARA_DATABASE_URL or DATABASE_URL first")
db = Database(database_url=settings.database_url, min_size=1, max_size=2)
try:
await db.connect()
assert await db.fetchval("SELECT 1") == 1
print("Database connection verified")
finally:
await db.disconnect()
if __name__ == "__main__":
asyncio.run(check_database())
This opens and closes its own pool and runs a read-only query. It verifies
connectivity, not migrations, application privileges, RLS or production
readiness. Run it as a separate process: constructing a Database also replaces
the process-wide instance used by Database.get_instance().
Connect the application lifespan¶
The basic scaffold passes settings.database_url into Aksara(...). Keep that
handoff: setting the global URL alone does not start the app's database
lifespan. Its normal startup connects the pool and normal shutdown disconnects
it. Connections acquired by supported database/session/transaction helpers are
returned according to those helpers' ownership rules.
Pool option names differ by interface:
| Interface | Minimum / maximum options |
|---|---|
configure(...) / global settings |
pool_min_size, pool_max_size |
Aksara(...) |
min_pool_size, max_pool_size |
Database(...) |
min_size, max_size |
The default minimum and maximum are 5 and 20. Pass the effective settings to the application constructor explicitly when overriding pool sizes; see the canonical configuration example. Budget connections across all web and worker processes, migrations and administrative clients. Measure concurrency and pool waits instead of choosing sizes from a traffic-label table.
Creating another Database is not transparent replica routing. It changes the
singleton and requires explicit connection/lifecycle ownership. An independent
connection also does not join an existing atomic transaction. See
transaction boundaries.
Apply migrations and serve¶
Follow the first-project tutorial for its exact model and migration sequence, or the domain template instructions for flat module copies. Review generated migration files before applying them. A successful connectivity probe does not mean application tables exist.
Continue to running your app after migrations and application authentication are configured.
Diagnose a connection failure¶
- Server unreachable: check the server process, host, port and network path.
- Authentication failure: verify the selected URL, role and PostgreSQL authentication policy. Do not weaken that policy merely to make a probe pass.
- Database missing: have the database created by an authorized role, or use the local helper with the required permission.
- Too many connections: account for every process's pool and server capacity before changing limits.
- TLS failure: use the database provider's certificate and hostname-verification configuration; encryption alone does not establish server identity.
Database connection setup can wrap the underlying error in
AksaraConnectionError; inspect the cause privately and redact connection
information before sharing it. The exception reference
explains the distinction between Aksara and driver errors. Aksara passes the DSN
to asyncpg; do not assume every libpq connection parameter is supported by that
driver.
For production, use the deployment guide: separate migration and application roles, restricted privileges, actual RLS policies where needed, secret management, diagnostics and backups.