# VPS Deployment Bundle

This bundle is generated by `scripts/package-for-vps.ps1` and deployed on Linux VPS by:

- `scripts/deploy-on-vps.sh` (recommended)
- `scripts/deploy-on-vps.ps1` (PowerShell 7 on Linux)

## Quick Start (Linux)

1. Copy bundle tar.gz to VPS.
2. Extract and enter directory.
3. Run:

```sh
bash scripts/deploy-on-vps.sh --bundle ./agomtradepro-vps-bundle-<tag>.tar.gz
```

## Recommended Packaging (Windows / PowerShell)

Default to the aggressive wrapper instead of calling `package-for-vps.ps1` directly. It retries common failure modes automatically:

- BuildKit grpc crash
- staged-context copy issues
- stale or incomplete Linux wheel cache
- offline wheelhouse missing a package
- optional local SQLite injection for full VPS restore

Code-only bundle:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1
```

Bundle with local SQLite:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1 -WithLocalSqlite
```

Prefer WSL build first:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1 -PreferWslBuild
```

Only use WSL strategies:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1 -WslOnly
```

What it tries, in order:

- BuildKit + cached wheelhouse
- legacy builder + cached wheelhouse
- legacy builder + refreshed wheelhouse
- legacy builder + refreshed wheelhouse + online pip fallback
- project-root context fallback
- WSL context fallback when available

It prints `FINAL_BUNDLE=...` on success.

## Wheel Cache

Linux wheel cache lives at:

```text
.cache/pip-wheels/linux-py311
```

Normal packaging reuses the cache automatically. Refresh only when dependency resolution fails or a required wheel is missing:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1 -RefreshWheelCache
```

If local wheel cache is still incomplete, allow network fallback:

```powershell
pwsh -File .\scripts\package-for-vps-aggressive.ps1 -AllowOnlinePipFallback
```

## Security Keys

`SECRET_KEY` and `AGOMTRADEPRO_ENCRYPTION_KEY` are **auto-generated on first boot** if not provided in `deploy/.env`:

- The entrypoint script generates missing keys before Django starts
- Keys are persisted to `/app/data/.env.generated` (inside the `sqlite_data` volume)
- All containers (web, celery_worker, celery_beat) share the same keys via the shared volume
- Keys survive container restarts and redeployments
- If you explicitly set keys in `deploy/.env`, those take precedence and are never overwritten by persisted generated values

To provide keys manually instead of auto-generating:

```bash
# Generate
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

# Add to deploy/.env
SECRET_KEY=<generated-value>
AGOMTRADEPRO_ENCRYPTION_KEY=<generated-value>
```

## Backup Critical VPS Secrets

Back up the current VPS `SECRET_KEY` and `AGOMTRADEPRO_ENCRYPTION_KEY` to a local ignored file:

```powershell
python .\scripts\backup-vps-secrets.py
```

Non-interactive example:

```powershell
python .\scripts\backup-vps-secrets.py --host your-vps-ip --port 22 --user root
```

Output directory:

```text
backups/vps-secrets/
```

This directory is ignored by git.

## Remote Build On VPS

When local Windows or WSL disk I/O is the bottleneck, use remote source build instead of local image packaging.

Script:

```powershell
python .\scripts\remote_build_deploy_vps.py
```

What it does:

- prompt for VPS host, port, username, password
- prompt whether to include local `db.sqlite3`
- prompt whether to wipe existing Docker resources first
- create a source tarball locally
- upload source tarball to VPS
- build `agomtradepro-web:<tag>` on the VPS
- deploy in-place with `docker compose`
- optionally restore local SQLite into the named volume before app startup
- run `migrate`, `bootstrap_cold_start`, and macro sync setup in one-off containers before long-running services start
- keep `celery_worker` and `celery_beat` from competing for SQLite startup migrations
- download a deployment report to `dist/remote-build-reports`
- remove remote temporary upload/build files by default

Non-interactive example:

```powershell
python .\scripts\remote_build_deploy_vps.py --host your-vps-ip --port 22 --user root --include-sqlite --wipe-docker --action fresh
```

Notes:

- this path avoids transferring large image tar files from local machine
- this path depends on the VPS having working Docker, Docker Compose, disk space, and outbound network access for Python package download during `docker build`
- source bundle is kept locally in `dist/`
- remote temp files are deleted unless `--keep-remote-temp` is used

## Backup and Restore (VPS)

Create backup:

```sh
bash scripts/vps-backup.sh --target-dir /opt/agomtradepro/current --backup-dir /opt/agomtradepro/backups --keep-days 14
```

Restore latest backup:

```sh
bash scripts/vps-restore.sh --target-dir /opt/agomtradepro/current --backup-dir /opt/agomtradepro/backups
```

## What is included

- Docker image tars for app + postgres + redis + caddy
- Docker image tars for app + redis + caddy + rsshub
- `docker/docker-compose.vps.yml`
- `docker/Caddyfile.template`
- `.env` template
- Optional backups:
  - `backups/db.sqlite3` (only when explicitly enabled during packaging)
  - `backups/dump.rdb` (only when explicitly enabled with `-RedisContainer`)
- `deploy/manifest.json` with SHA-256 checksums
- `scripts/verify-vps-bundle.ps1` for local bundle verification

## Notes

- Scripts are ASCII-only.
- Domain is optional. If omitted, Caddy is configured for HTTP only (`:80`).
- App runtime uses `core.settings.production`.
- Package script defaults to "code upgrade bundle" (no local DB/Redis data).
- `scripts/package-for-vps-aggressive.ps1` is the recommended packaging entrypoint on Windows.
- `ENABLE_RSSHUB=true` enables self-hosted RSSHub service in the stack.
- `ENABLE_CELERY=false` is recommended on 4G VPS, enable only when needed.
- Production image installs from `requirements-prod.txt`.
