Install and deploy
The application is a single Python 3.12 service with a SQLite file. It runs on any Linux
box, a Docker host, or a laptop. Nothing about it depends on the developer's accounts,
servers or keys: every credential is yours and lives in your .env and your data folder.
Option A: Docker Compose (recommended)
Requirements: Docker 24+ with Compose.
git clone <your repository> deanreach
cd deanreach
cp .env.example .env
# edit .env: ADMIN_USER, ADMIN_PASSWORD, SECRET_KEY (see below), TRANSPORT=gmail
docker compose up -d --build
Two containers start: web (the admin on port 8000) and worker (the scheduler). Data
lives in the ./data folder on the host: the SQLite database, the Gmail token, uploaded CVs
and the OAuth client file. Back that folder up (see Backup and recovery).
Generate a SECRET_KEY:
python3 -c "import secrets; print(secrets.token_hex(32))"
Open http://<server>:8000, sign in with ADMIN_USER / ADMIN_PASSWORD, then connect Gmail
from Settings (see Gmail and Google Cloud setup).
Option B: bare metal with systemd
sudo apt install python3.12 python3.12-venv
git clone <your repository> /opt/deanreach
cd /opt/deanreach
python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt -r requirements-dev.txt
cp .env.example .env # then edit it
DATA_DIR=/opt/deanreach/data .venv/bin/uvicorn app:app --host 127.0.0.1 --port 8000
A systemd unit (/etc/systemd/system/deanreach.service):
[Unit]
Description=DeanReach outreach admin
After=network-online.target
[Service]
User=deanreach
WorkingDirectory=/opt/deanreach
EnvironmentFile=/opt/deanreach/.env
Environment=SCHEDULER_ENABLED=1
ExecStart=/opt/deanreach/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8000 --workers 1
Restart=always
[Install]
WantedBy=multi-user.target
Keep --workers 1 when the scheduler runs inside the web process (only one process may own
the schedule). To scale the web side, set SCHEDULER_ENABLED=0 there and run
python -m deanreach.worker as a second unit.
HTTPS and the public address
Put a reverse proxy with TLS in front (needed for the Google OAuth callback). Caddy does it in two lines:
outreach.example.com {
reverse_proxy 127.0.0.1:8000
}
The OAuth redirect URI you register with Google is then
https://outreach.example.com/settings/gmail/callback.
Environment variables
All of them are documented in .env.example. The ones that matter on day one:
| Variable | Meaning |
|---|---|
ADMIN_USER, ADMIN_PASSWORD |
the dashboard login |
SECRET_KEY |
signs the session cookie; change it and every session logs out |
DATA_DIR |
folder for the database, token, uploads (default ./data) |
DATABASE_URL |
leave unset for SQLite; set postgresql+psycopg://... for Postgres (install psycopg[binary]) |
TRANSPORT |
gmail to send, dryrun to build and log only |
GMAIL_CLIENT_SECRETS |
path of the OAuth client JSON from Google Cloud |
ANTHROPIC_API_KEY |
optional; enables the model fallback for unusual page layouts |
SCHEDULER_ENABLED |
1 to run the cron schedule in this process |
LIVE_FETCH_ENABLED |
0 to forbid fetching arbitrary URLs from the admin |
First run checklist
- Sign in, open Settings, set the sender name, signature and the Gmail address.
- Connect Gmail (Settings) after completing the Google Cloud steps.
- Upload the CV (Settings), or keep the file at
CV_PATH. - Review Targets (titles, disciplines) and Templates.
- Add source pages (Source pages), crawl one, read the run trace.
- Set the caps and the schedule in Settings. Run outreach once by hand with
TRANSPORT=dryrunto see what would go out, then switch togmail.
Updating
cd /opt/deanreach && git pull
.venv/bin/pip install -r requirements.txt
sudo systemctl restart deanreach
Schema changes are applied automatically at start (create_all); columns are never dropped.
Take a backup before updating anyway.
Running the tests
.venv/bin/python -m pytest -q