IntelliPlan ships with everything needed for a production deployment: aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/UAnirudh/IntelliPlan/llms.txt
Use this file to discover all available pages before exploring further.
Procfile for Railway, a Dockerfile for any container host, and a gunicorn.conf.py that reads $PORT at runtime so the start command stays shell-independent. This page walks through each deployment path, explains the PostgreSQL requirements, documents the production security checklist, and covers the cron jobs that power push notifications and lifecycle emails.
Railway Deployment
Railway is the recommended hosting platform. It provisions a PostgreSQL database, injectsDATABASE_URL automatically, and the Procfile tells it exactly how to start the app.
Fork and connect the repository
Fork github.com/UAnirudh/IntelliPlan to your GitHub account, then create a new Railway project and connect the forked repository. Railway detects the Using
Procfile and uses it as the start command.The Procfile contains:python -m gunicorn instead of the gunicorn console script bypasses any virtualenv shebang issues that caused Nixpacks-built containers to crash at start.Add a PostgreSQL plugin
In your Railway project, click + New → Database → PostgreSQL. Railway injects
DATABASE_URL as an environment variable that Flask-SQLAlchemy picks up automatically — no extra configuration needed.Set required environment variables
In the Railway service’s Variables tab, add at minimum:
DATABASE_URL is already set by the PostgreSQL plugin. Add GROQ_API_KEY as well — the Gemini free tier allows only 20 requests per day, and without a fallback every AI feature stops when that quota runs out.Add production security variables
These variables are optional at boot time but required for a production deployment:Generate Generate
DATA_ENCRYPTION_KEY with:CRON_SECRET with:Docker Deployment
IntelliPlan includes aDockerfile for any container host that supports OCI images (Fly.io, Render, DigitalOcean App Platform, a bare VPS with Docker installed, etc.).
Dockerfile
Gunicorn Configuration
gunicorn.conf.py controls the process model. The defaults are conservative and suitable for a Railway starter plan:
gunicorn.conf.py
WEB_CONCURRENCY via an environment variable to match your plan’s available CPU. max_requests + max_requests_jitter recycles workers periodically to prevent memory leaks from accumulating across long-lived processes.
Database: PostgreSQL vs SQLite
| Scenario | Recommended database |
|---|---|
| Local development | sqlite:///intelliplan.db — zero setup, file-based |
| Railway / any production | PostgreSQL via DATABASE_URL=postgresql://... |
| Docker on a VPS | PostgreSQL container or managed service |
The
psycopg2-binary package in requirements.txt provides the PostgreSQL driver. No separate installation is needed; it ships as a binary wheel for all supported platforms.Generating VAPID Keys for Push Notifications
Browser push notifications require a VAPID key pair generated once and stored permanently. IntelliPlan shipsvapid.py to produce them in the correct format:
.env:
VAPID_EMAIL must be an address a push service can actually reach you at — it is sent as the sub claim in the VAPID JWT, and some push services reject deliveries without it.
Encryption at Rest
DATA_ENCRYPTION_KEY encrypts the third-party OAuth tokens IntelliPlan stores (Canvas, Google, Notion, Blackboard, Moodle). Without it, those tokens are kept in plaintext — a database backup hands over live access to every connected student account.
Rotating Keep this key in a secret store — never in git.
DATA_ENCRYPTION_KEY invalidates all existing stored tokens. Affected students must reconnect their school platform accounts. To rotate safely, put the new key first in a comma-separated list; IntelliPlan reads tokens with any listed key and always writes with the first. Then re-encrypt existing tokens:Production Checklist
Before sending IntelliPlan live to real students, confirm each item below:Security variables
Security variables
| Variable | Purpose | How to generate |
|---|---|---|
SECRET_KEY | Flask session signing | python -c "import secrets; print(secrets.token_hex(32))" |
DATA_ENCRYPTION_KEY | Encrypts stored OAuth tokens | python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" |
CRON_SECRET | Authenticates cron endpoint calls | python -c "import secrets; print(secrets.token_hex(32))" |
VAPID_PRIVATE_KEY | Signs Web Push notifications | python vapid.py |
AI keys
AI keys
| Variable | Required? | Notes |
|---|---|---|
GEMINI_API_KEY | Yes | Primary model for all AI features |
GROQ_API_KEY | Strongly recommended | Fallback when Gemini quota is exhausted |
ANTHROPIC_API_KEY | Optional | Paid-plan model only |
Database
Database
DATABASE_URLmust point to a persistent PostgreSQL instance.- Flask-Session requires a persistent database — SQLite on an ephemeral container filesystem loses all sessions on restart.
- The
psycopg2-binarydriver is already inrequirements.txt.
App base URL
App base URL
Set
APP_BASE_URL to your public domain (e.g. https://intelliplan.tech). OAuth redirect URIs for Google, Canvas, Notion, and Blackboard are constructed from this value. A mismatch causes OAuth callbacks to fail silently with invalid_redirect_uri.Error tracking (optional but recommended)
Error tracking (optional but recommended)
Set
SENTRY_DSN to a Sentry project DSN to capture unhandled exceptions in production. The Flask Sentry integration is already wired in App.py and activates automatically when the variable is present.Cron Jobs
IntelliPlan has two families of cron endpoints. Both accept either theX-Cron-Secret or X-Cron-Token header, plus a ?secret= query parameter fallback.
Push Notifications
The notification outbox must be drained every few minutes. Point a scheduler at:*/5 * * * * (every 5 minutes). The endpoint deduplicates on a UNIQUE constraint and claims rows before sending, so overlapping runs never double-send.
A 401 response means the secret is missing or wrong. A 503 means CRON_SECRET is unset. Both indicate the endpoint is working — the issue is in the configuration.
Lifecycle Emails
Three lifecycle emails are available: a welcome email (transactional), a feedback request (14-day opt-in users), and a weekly newsletter (opt-in, auto-generated). All requireRESEND_API_KEY and MARKETING_POSTAL_ADDRESS to be set.
Daily lifecycle sweep — runs the welcome email and feedback-request logic:
Assignment Reminders
Environment Variables Reference
The table below covers every variable needed for a complete production deployment. All are optional at boot time exceptSECRET_KEY, GEMINI_API_KEY, and DATABASE_URL.