A fast, type-safe backend service built with Rust, Axum, and Diesel. Secure APIs for notes, bookmarks, and todos.
- 🚀 High-performance Rust backend with Axum
- 🔒 Type-safe database operations with Diesel
- 🐳 Production-ready Docker setup with multi-stage builds
- 🛡️ Security-hardened distroless runtime image (no default credentials!)
- 📦 Docker Compose orchestration for local development
- 🔐 JWT authentication with mandatory secret configuration
- 📊 PostgreSQL database
- 🔒 Environment-based configuration (no hardcoded secrets)
-
Install Dependencies
# Install Rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Install Diesel CLI cargo install diesel_cli --no-default-features --features postgres
-
Setup Database
# Start PostgreSQL (or use Docker Compose) docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16-alpine # Run migrations diesel migration run
-
Configure Environment
# REQUIRED: Create .env file from example cp .env.example .env # Edit .env and set secure values: # - DATABASE_URL with your PostgreSQL credentials # - JWT_SECRET (minimum 32 characters) # Generate JWT secret: openssl rand -base64 32
-
Run the Application
cargo run --release
IMPORTANT: Security Setup Required
Before running, you MUST create a .env file with your secure credentials:
# Copy the example environment file
cp .env.example .env
# Edit .env and set secure values for:
# - POSTGRES_USER
# - POSTGRES_PASSWORD
# - JWT_SECRET (minimum 32 characters)
# NEVER use the example values in production!Generate secure secrets:
# Generate a strong JWT secret
openssl rand -base64 32
# Generate a strong password
pwgen -s 32 1
# Or on Windows PowerShell:
# [System.Web.Security.Membership]::GeneratePassword(32,10)Then start the services:
# Start all services (backend + database)
docker-compose up -d
# View logs
docker-compose logs -f ferrite
# Stop services
docker-compose downThe project uses a secure two-stage build process:
Stage 1: Build (rust:1.83-bookworm)
- Compiles Rust application with full optimizations
- Leverages layer caching for dependencies
- Strips binary to reduce size
Stage 2: Runtime (distroless/cc-debian12:nonroot)
- Minimal image with only runtime dependencies
- No shell, package manager, or unnecessary tools
- Runs as non-root user (UID 65532)
- Significantly reduced attack surface
- ✅ Non-root user (UID 65532)
- ✅ Distroless base image
- ✅ Stripped binary
- ✅ No privilege escalation
- ✅ Minimal attack surface
# Build the image
docker build -t ferrite:latest .
# Build with specific Rust version
docker build --build-arg RUST_VERSION=1.83 -t ferrite:latest .# Start all services
docker-compose up -d
# Start with PgAdmin for database management
docker-compose --profile tools up -d
# View logs
docker-compose logs -f ferrite
# Stop services
docker-compose down
# Remove volumes (WARNING: deletes data)
docker-compose down -v# SECURITY: Never use these example values!
# Set real credentials in your environment or .env file
docker run -d \
--name ferrite-backend \
-p 8080:8080 \
-e DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@host:5432/${POSTGRES_DB}" \
-e JWT_SECRET="${JWT_SECRET}" \
-e RUST_LOG=info \
ferrite:latestPostgreSQL Database
- Image:
postgres:16-alpine - Port: 5432
- Persistent volume for data
- Health checks enabled
Ferrite Backend
- Multi-stage build from Dockerfile
- Port: 8080
- Waits for healthy database
- Non-root user with security hardening
PgAdmin (Optional)
- Port: 5050
- Access: http://localhost:5050
- Start with:
docker-compose --profile tools up
SECURITY CRITICAL: All values are REQUIRED
-
Copy the example file:
cp .env.example .env
-
Edit
.envand provide secure values:
# Database - Use strong credentials
POSTGRES_USER=your_db_username
POSTGRES_PASSWORD=your_secure_password
POSTGRES_DB=ferrite
POSTGRES_PORT=5432
# Application
APP_PORT=8080
HOST=0.0.0.0
PORT=8080
# JWT Secret - MUST be at least 32 characters
# Generate with: openssl rand -base64 32
JWT_SECRET=your_secure_jwt_secret_at_least_32_characters
# Logging
RUST_LOG=info,ferrite=debug
RUST_BACKTRACE=1
# PgAdmin (only if using --profile tools)
PGADMIN_EMAIL=admin@ferrite.local
PGADMIN_PASSWORD=your_pgadmin_password
PGADMIN_PORT=5050Security Best Practices:
- ✅ Never commit
.envfiles to version control - ✅ Use strong, randomly generated passwords
- ✅ Generate JWT secret:
openssl rand -base64 32 - ✅ Rotate secrets regularly
- ✅ Set restrictive file permissions:
chmod 600 .env - ✅ Use different credentials for dev/staging/production
# Option 1: Using diesel CLI in container with environment variables
docker run --rm \
--network ferrite_ferrite-network \
-v $(pwd)/migrations:/migrations \
-e DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}" \
rust:1.83-bookworm \
bash -c "cargo install diesel_cli --no-default-features --features postgres && diesel migration run"
# Option 2: Exec into running container
docker-compose exec ferrite /bin/sh- Rust 1.83 or higher
- PostgreSQL 12+
- Diesel CLI:
cargo install diesel_cli --no-default-features --features postgres
ferrite/
├── src/
│ ├── main.rs # Application entry point
│ ├── db.rs # Database connection
│ ├── errors.rs # Error handling
│ ├── models/ # Data models
│ ├── routes/ # API routes
│ └── security/ # Security middleware
├── migrations/ # Database migrations
├── Dockerfile # Multi-stage Docker build
├── docker-compose.yml # Local development orchestration
└── .env # Environment variables
# Run all tests
cargo test
# Run with coverage
cargo test --all-features
# Run in Docker
docker build --target builder -t ferrite:test .
docker run --rm ferrite:test cargo test# Format code
cargo fmt
# Lint code
cargo clippy --all-targets --all-features -- -D warnings
# Run security audit
cargo auditFerrite requires explicit configuration of all security-sensitive environment variables. The application will NOT start with defaults.
Required: See SECURITY.md for comprehensive security guidelines.
-
Create environment file:
cp .env.example .env
-
Generate secure secrets:
# JWT Secret (minimum 32 characters) openssl rand -base64 32 # Strong password pwgen -s 32 1
-
Edit
.envwith your secrets (NEVER commit this file!)
Dependency Auditing
- Install:
cargo install --locked cargo-audit - Run:
cargo auditfrom repository root - The
audit.tomlenforces database freshness and fails on warnings - To silence specific advisories, add
RUSTSEC-ID inaudit.toml
Static Analysis
- Run
cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic -W clippy::nursery -W clippy::future_not_send -W clippy::print_stdout -W clippy::print_stderr -W clippy::dbg_macro clippy.tomlbansunwrap/expect, unchecked indexing, anddbg!usage
- Generate strong, unique JWT_SECRET (32+ characters)
- Use secure database passwords (16+ characters)
- Never commit
.envfiles to version control - Keep base images updated regularly
- Run security scans:
docker scan ferrite:latest - Enable HTTPS/TLS in production
- Configure rate limiting at reverse proxy
- Set up monitoring and security alerts
- Use Docker secrets or vault in production
- Rotate credentials regularly
For detailed security practices, see SECURITY.md.
cargo testcargo test --test '*'# Example with wrk
wrk -t4 -c100 -d30s http://localhost:8080/health# Check container status
docker-compose ps
# View resource usage
docker stats
# Check logs
docker-compose logs -f# Export
docker-compose exec postgres pg_dump -U postgres ferrite > backup.sql
# Restore
docker-compose exec -T postgres psql -U postgres ferrite < backup.sqlDatabase connection errors
# Check PostgreSQL health
docker-compose ps postgres
# Verify DATABASE_URL in .env
echo $DATABASE_URLBuild failures
# Clear build cache
cargo clean
docker builder prune
# Rebuild without cache
docker build --no-cache -t ferrite:latest .Permission errors in Docker
- Ensure migrations directory is readable
- Check volume mount permissions
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request
See LICENSE file for details.