A collaborative web platform for training ML models, built with Flask and containerized with Docker.
- Team-based collaboration — Create teams and share projects with teammates via invite codes
- Tabular ML — Train classification and regression models on CSV data (scikit-learn)
- Image classification — Train image classifiers on labeled image folders
- Model export — Export as pickle, ONNX, or universal (dependency-free) formats
- Model serving — REST API to make predictions on any model
- ONNX import — Bring your own ONNX models and use them in Anvil
# Install dependencies
pip install -r requirements.txt
# Run locally (Flask development server)
python app.py
# Access: http://localhost:5000# Build image
docker build -t anvil-forger .
# Run container
docker run -d -p 5000:5000 \
-v $(pwd)/data:/app/data \
-e ANVIL_SECRET_KEY=your-secret-key \
anvil-forger# Development (with hot-reload)
docker-compose up
# Production
docker-compose -f docker-compose.prod.yml up -dThis project includes three automated GitHub Actions workflows:
File: .github/workflows/docker-build-push.yml
Triggers on:
pushtomainbranchpull_requesttomainbranch- Manual trigger (
workflow_dispatch)
Actions:
- Builds multi-stage Docker image
- On PR: builds only (no push)
- On push to main: builds and pushes to Docker Hub with tags:
latest(default branch)main(branch name)- Git SHA prefix
- Semantic version tags (if tagged)
Required Secrets:
DOCKERHUB_USERNAME— Your Docker Hub usernameDOCKERHUB_TOKEN— Your Docker Hub Personal Access Token (not password!)
File: .github/workflows/ghcr-build-push.yml
Triggers on:
pushtomainbranch- Manual trigger (
workflow_dispatch)
Actions:
- Builds and pushes to GitHub Container Registry (
ghcr.io) - Uses GitHub's built-in
GITHUB_TOKEN(no additional secrets needed)
Required Secrets:
- None (uses
secrets.GITHUB_TOKENautomatically)
File: .github/workflows/tests.yml
Triggers on:
pushtomainordeveloppull_requesttomainordevelop- Manual trigger (
workflow_dispatch)
Actions:
- Python 3.12 test matrix
- Installs dependencies from
requirements.txt - Runs linting (flake8)
- Runs pytest with coverage
- Uploads coverage to Codecov
- Builds Docker image and runs health check
- Checks image size
Required Secrets:
- None (optional: Codecov token for coverage reports)
- Create a Docker Hub account: https://hub.docker.com/signup
- Generate a Personal Access Token:
- Log in to Docker Hub
- Account Settings → Security → New Access Token
- Copy the token immediately
- Add to GitHub:
- Go to your repository → Settings → Secrets and variables → Actions
- Click "New repository secret"
- Add
DOCKERHUB_USERNAMEwith your Docker Hub username - Add
DOCKERHUB_TOKENwith the token you copied
No additional setup needed! GitHub automatically provides secrets.GITHUB_TOKEN.
docker pull ayibongwe02/anvil-forger:latest
docker run -d -p 5000:5000 \
-v anvil-data:/app/data \
-e ANVIL_SECRET_KEY=your-secret \
ayibongwe02/anvil-forger:latestdocker pull ghcr.io/Ayibongwe02/Anvil-Forger:latest
docker run -d -p 5000:5000 \
-v anvil-data:/app/data \
-e ANVIL_SECRET_KEY=your-secret \
ghcr.io/Ayibongwe02/Anvil-Forger:latest.
├── .github/workflows/ # GitHub Actions workflows
│ ├── docker-build-push.yml # Docker Hub build & push
│ ├── ghcr-build-push.yml # GHCR build & push
│ ├── tests.yml # Tests & linting
│ └── deploy-railway.yml # Railway deployment
├── src/ # Source modules
│ ├── db.py # SQLite database
│ ├── auth.py # Authentication & teams
│ ├── tabular_training.py # Tabular ML training
│ ├── image_training.py # Image classification
│ ├── export_bundle.py # Model export
│ ├── onnx_import.py # ONNX model import
│ └── api_serving.py # REST API blueprint
├── templates/ # Flask templates
├── static/ # CSS, JavaScript
├── data/ # Datasets & models (volume mount)
├── app.py # Flask entry point
├── Dockerfile # Production multi-stage build
├── docker-compose.yml # Development compose config
├── docker-compose.prod.yml # Production compose config
├── requirements.txt # Python dependencies
└── .dockerignore # Docker build optimization
ANVIL_SECRET_KEY=dev-secret-key-change-in-production
PYTHONUNBUFFERED=1
PYTHONDONTWRITEBYTECODE=1See .env.production.example:
ANVIL_SECRET_KEY=<strong-random-secret>
FLASK_ENV=production
DEBUG=FalseAnvil uses SQLite (single-writer constraint) for simplicity. Data persists in /app/data/ (mounted as a volume).
For multi-server deployments, migrate to PostgreSQL (edit src/db.py).
- ✅ Non-root user (
anvil:anvil) - ✅ Multi-stage Docker build (no build tools in runtime)
- ✅ Secrets via environment variables (not baked into image)
- ✅ Health checks for automatic recovery
- ✅ Resource limits (configurable in compose files)
| Metric | Value | Notes |
|---|---|---|
| Image Size | ~924 MB | Includes all ML dependencies |
| Gunicorn Workers | 1 | SQLite single-writer constraint |
| Memory Limit | 2 GB (prod) | Adjustable |
| CPU Limit | 2 cores (prod) | Adjustable |
| Health Check | 30s interval | Tunable |
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit changes (
git commit -am 'Add feature') - Push to branch (
git push origin feature/my-feature) - Open a Pull Request
All PRs trigger:
- Linting (flake8)
- Tests (pytest)
- Docker build (no push)
Merge to main triggers:
- Build and push to Docker Hub
- Build and push to GHCR
- Deploy to Railway (if configured)
MIT
For issues or questions, open a GitHub issue or check the documentation in this repository.