Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.git
.gitignore
.venv
venv*
__pycache__
*.pyc
.DS_Store
*.egg-info
.cache
.idea
celerybeat-*
media/*
*.md
!CLAUDE.md
docs/
selenium/
deploy-staging.sh
reset.sh
settings_ci.py
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dockerfile for Fly.io deployment
# Also usable for any Docker-based deployment.
# Heroku deployment continues to use the Procfile and does not use this file.

FROM python:3.13-slim AS base

# Install system dependencies required by python-ldap and psycopg2
RUN apt-get update && apt-get install -y --no-install-recommends \
libldap2-dev \
libsasl2-dev \
libpq-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

# Install dependencies first (layer caching)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

# Copy application code
COPY . .

# Default port (Fly.io sets PORT=8080 by default)
ENV PORT=8080

EXPOSE 8080

# Run gunicorn via uv so it picks up the virtual environment
CMD ["sh", "-c", "uv run gunicorn wsgi:application -b 0.0.0.0:$PORT -w 4"]
57 changes: 57 additions & 0 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Fly.io deployment configuration for Helios Voting
#
# To deploy:
# 1. Install flyctl: https://fly.io/docs/flyctl/install/
# 2. Create the app: fly apps create your-app-name
# 3. Create a Postgres database: fly postgres create
# 4. Attach it: fly postgres attach your-db-name
# 5. Set secrets:
# fly secrets set SECRET_KEY="your-secret-key"
# fly secrets set ALLOWED_HOSTS="your-app-name.fly.dev"
# fly secrets set URL_HOST="https://your-app-name.fly.dev"
# fly secrets set SSL=1
# fly secrets set HSTS=1
# fly secrets set DEBUG=0
# 6. Deploy: fly deploy
#
# For background worker (Celery), scale a separate process group:
# fly scale count worker=1
#
# Note: Fly Postgres uses internal networking without SSL,
# so DATABASE_SSL_REQUIRE defaults to 0 (override via env if needed).

app = "helios-voting"
primary_region = "iad"

[build]

[env]
PORT = "8080"
# Fly Postgres connects over internal network without SSL
DATABASE_SSL_REQUIRE = "0"

[processes]
web = "uv run gunicorn wsgi:application -b 0.0.0.0:8080 -w 4"
worker = "uv run celery --app helios worker --events --beat --concurrency 1"

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0
processes = ["web"]

[http_service.concurrency]
type = "connections"
hard_limit = 250
soft_limit = 200

[[vm]]
memory = "512mb"
cpu_kind = "shared"
cpus = 1

[[statics]]
guest_path = "/app/server_ui/media"
url_prefix = "/static/"
3 changes: 2 additions & 1 deletion settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def get_from_env(var, default):
# override if we have an env variable
if get_from_env('DATABASE_URL', None):
import dj_database_url
DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)
_db_ssl = get_from_env('DATABASE_SSL_REQUIRE', '1') == '1'
DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=_db_ssl)
DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'

# explicitly set the default auto-created primary field to silence warning models.W042
Expand Down