Make cycle button match PaymentFilterComponent and Columns button style. Show 'Current Cycle Payment Status' or 'Last Cycle Payment Status' based on active state. Button shows active state when current cycle is selected. |
||
|---|---|---|
| .forgejo | ||
| assets | ||
| config | ||
| docs | ||
| lib | ||
| priv | ||
| rel/overlays/bin | ||
| test | ||
| .credo.exs | ||
| .dockerignore | ||
| .drone.yml | ||
| .editorconfig | ||
| .env.example | ||
| .formatter.exs | ||
| .gitignore | ||
| .igniter.exs | ||
| .sobelow-conf | ||
| .tool-versions | ||
| CHANGELOG.md | ||
| CODE_GUIDELINES.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Justfile | ||
| LICENSE | ||
| mix.exs | ||
| mix.lock | ||
| README.md | ||
| renovate.json | ||
| renovate_backend_config.js | ||
Mila
Mila — simple, usable, self-hostable membership management for small to mid-sized clubs.
🚧 Project Status
⚠️ Early development — not production-ready. Expect breaking changes.
Contributions and feedback are welcome!
✨ Overview
Mila is a free and open-source membership management tool designed for real club needs.
It is self-hosting friendly, aims for accessibility and GDPR compliance, and focuses on usability instead of feature overload.
💡 Why Mila?
Most membership tools for clubs are either:
- Too complex — overloaded with features small and mid-sized clubs don’t need
- Too expensive — hidden fees, closed ecosystems, vendor lock-in
- Too rigid — no way to adapt fields, processes, or roles to your club’s reality
Mila is different:
- Simple: Focused on what clubs really need — members, dues, communication.
- Usable: Clean, accessible UI, GDPR-compliant, designed with everyday volunteers in mind.
- Flexible: Customize what data you collect about members, role-based permissions, and self-service for members.
- Truly open: 100% free and open source — no lock-in, transparent code, self-host friendly.
Our philosophy: software should help people spend less time on administration and more time on their community.
📸 Screenshots

This is how Mila might look in action.
🔑 Features
- ✅ Manage member data with ease
- 🚧 Overview of membership fees & payment status
- ✅ Full-text search
- 🚧 Sorting & filtering
- 🚧 Roles & permissions (e.g. board, treasurer)
- ✅ Custom fields (flexible per club needs)
- ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
- 🚧 Self-service & online application
- 🚧 Accessibility, GDPR, usability improvements
- 🚧 Email sending
🚀 Quick Start (Development)
Prerequisites
We recommend using asdf for managing tool versions.
- Tested with:
asdf 0.16.5 - Required versions are documented in
.tool-versionsin this repo
Install system dependencies (Debian/Ubuntu)
# Debian 12
apt-get -y install build-essential autoconf m4 libncurses-dev libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils openjdk-17-jdk icu-devtools bison flex pkg-config
# Ubuntu 24
apt-get -y install build-essential autoconf m4 libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils libncurses-dev openjdk-11-jdk icu-devtools bison flex libreadline-dev
Install asdf
mkdir ~/.asdf
cd ~/.asdf
wget https://github.com/asdf-vm/asdf/releases/download/v0.16.5/asdf-v0.16.5-linux-amd64.tar.gz
tar -xvf asdf-v0.16.5-linux-amd64.tar.gz
ln -s ~/.asdf/asdf ~/.local/bin/asdf
Then follow the official “Shell Configuration” steps in the asdf docs.
Fish example (~/.config/fish/config.fish):
asdf completion fish > ~/.config/fish/completions/asdf.fish
set -gx PATH "$HOME/.asdf/shims" $PATH
Bash example (~/.bash_profile and ~/.bashrc):
export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"
. <(asdf completion bash)
Install project dependencies
git clone https://git.local-it.org/local-it/mitgliederverwaltung.git mila
cd mila
asdf install
# Inside the repo folder:
mix local.hex
mix archive.install hex phx_new
Note: running
mix local.hexmust be done inside the repo folder,
because.tool-versionsdefines the Erlang/Elixir versions.
Run the app
-
Copy env file:
cp .env.example .env # Set OIDC_CLIENT_SECRET inside .env -
Start everything (database, Mailcrab, Rauthy, app):
just run -
Services will be available at:
- App: http://localhost:4000
- Mail UI: http://localhost:1080
- Postgres:
localhost:5000
🔐 Testing SSO locally
Mila uses OIDC for Single Sign-On. In development, a local Rauthy instance is provided.
just run- go to localhost:8080, go to the Admin area
- Login with "admin@localhost" and password from
BOOTSTRAP_ADMIN_PASSWORD_PLAINin docker-compose.yml - add client from the admin panel
- Client ID: mv
- redirect uris: http://localhost:4000/auth/user/rauthy/callback
- Authorization Flows: authorization_code
- allowed origins: http://localhost:4000
- access/id token algortihm: RS256 (EDDSA did not work for me, found just few infos in the ashauthentication docs)
- copy client secret to
.envfile - abort and run
just runagain
Now you can log in to Mila via OIDC!
OIDC with other providers (Authentik, Keycloak, etc.)
Mila works with any OIDC-compliant provider. The internal strategy is named :rauthy, but this is just a name — it works with any provider.
Important: The redirect URI must always end with /auth/user/rauthy/callback.
Example for Authentik:
- Create an OAuth2/OpenID Provider in Authentik
- Set the redirect URI to:
https://your-domain.com/auth/user/rauthy/callback - Configure environment variables:
DOMAIN=your-domain.com # or PHX_HOST=your-domain.com OIDC_CLIENT_ID=your-client-id OIDC_BASE_URL=https://auth.example.com/application/o/your-app OIDC_CLIENT_SECRET=your-client-secret # or use OIDC_CLIENT_SECRET_FILE
The OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/rauthy/callback if not explicitly set.
⚙️ Configuration
- Env vars: see
.env.exampleOIDC_CLIENT_SECRET— secret for your OIDC client
- Database defaults (Docker Compose):
- Host:
localhost - Port:
5000 - User/pass:
postgres/postgres - DB:
mila_dev
- Host:
🏗️ Architecture
Tech Stack Overview:
- Backend: Elixir + Phoenix + Ash Framework
- Frontend: Phoenix LiveView + Tailwind CSS + DaisyUI
- Database: PostgreSQL
- Auth: AshAuthentication (OIDC + password)
Code Structure:
lib/accounts/&lib/membership/— Ash resources and domainslib/mv_web/— Phoenix controllers, LiveViews, componentsassets/— Tailwind, JavaScript, static files
📚 Full tech stack details: See CODE_GUIDELINES.md
📖 Implementation history: See docs/development-progress-log.md
🗄️ Database schema: See docs/database-schema-readme.md
🧑💻 Development
Common commands:
just run # Start full dev environment
just test # Run test suite
just lint # Code style checks
just audit # Security audits
just reset-database # Reset local DB
📚 Full development guidelines: See CODE_GUIDELINES.md
📦 Production Deployment
Local Production Testing
For testing the production Docker build locally:
-
Generate secrets:
mix phx.gen.secret # for SECRET_KEY_BASE mix phx.gen.secret # for TOKEN_SIGNING_SECRET -
Create
.envfile:# Copy template and edit cp .env.example .env nano .env # Required variables: SECRET_KEY_BASE=<your-generated-secret> TOKEN_SIGNING_SECRET=<your-generated-secret> DOMAIN=localhost # or PHX_HOST=localhost # Optional OIDC configuration: # OIDC_CLIENT_ID=mv # OIDC_BASE_URL=http://localhost:8080/auth/v1 # OIDC_CLIENT_SECRET=<from-your-oidc-provider> # OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/rauthy/callback # Alternative: Use _FILE variables for Docker secrets (takes priority over regular vars): # SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base # TOKEN_SIGNING_SECRET_FILE=/run/secrets/token_signing_secret # OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc_client_secret # DATABASE_URL_FILE=/run/secrets/database_url # DATABASE_PASSWORD_FILE=/run/secrets/database_password -
Start development environment (for Rauthy):
docker compose up -d -
Start production environment:
docker compose -f docker-compose.prod.yml up -
Database migrations run automatically on app start. For manual migration:
docker compose -f docker-compose.prod.yml exec app /app/bin/mv eval "Mv.Release.migrate" -
Access the production app:
- Production App: http://localhost:4001
- Uses same Rauthy instance as dev (localhost:8080)
Note: The local production setup uses network_mode: host to share localhost with the development Rauthy instance. For real production deployment, configure an external OIDC provider and remove network_mode: host.
Real Production Deployment
For actual production deployment:
- Use an external OIDC provider (not the local Rauthy)
- Update
docker-compose.prod.yml:- Remove
network_mode: host - Set
OIDC_BASE_URLto your production OIDC provider - Configure proper Docker networks
- Remove
- Set up SSL/TLS (e.g., via reverse proxy like Nginx/Traefik)
- Use secure secrets management — All sensitive environment variables support a
_FILEsuffix for Docker secrets (e.g.,SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base). Seedocker-compose.prod.ymlfor an example setup with Docker secrets. - Configure database backups
🤝 Contributing
We welcome contributions!
- Open issues and PRs in this repo.
- Please follow existing code style and conventions.
- Expect breaking changes while the project is in early development.
📄 License
License: AGPLv3
See the LICENSE file for details.
📬 Contact
- Issues: GitLab Issues
- Community links: coming soon.