TLDR: DailyFX is a self-hosted web app for Immich that automatically picks photos, applies creative effects or AI transformations, lets you review the results, and uploads accepted images back to Immich with metadata preserved.
DailyFX is a self-hosted creative companion designed specifically for Immich. It breathes new life into your photo archives by automatically transforming your pictures into stunning artistic masterpieces—from vintage film strips and pop-art collages to next-gen AI-generated transformations. Review them daily, approve your favorites with one click, and seamlessly upload them back to Immich with all original EXIF metadata (camera, GPS, and timestamp) perfectly preserved.
Unlike generic photo filters that strip away metadata or require tedious manual work, DailyFX acts as an automated background curator. It connects directly to your Immich instance, retrieves assets based on your custom schedules, and applies any of its 40+ built-in creative modules. You can choose classic darkroom simulations like Cyanotype and Polaroid, high-dynamic-range enhancements, or leverage state-of-the-art Generative AI models to reimagine your photos in comic book, cyberpunk, or claymation styles.
The entire process is designed with the self-hoster in mind. Running completely on your own hardware via Docker, it ensures your private memories remain entirely under your control unless you explicitly enable external cloud-based AI features. It features a metadata pipeline that supports AI-powered caption and title generation, automatically analyzing the final image so the stored title, summary, and tags match what you actually review. For more information on data sharing, see the Data Privacy & Cloud AI Providers section below.
Once an effect is generated, you are notified through your preferred channel, whether that is an interactive Telegram message, a Home Assistant alert, a Gotify/ntfy notification, or a standard Web Push. With a single tap from the notification, you can compare the original and styled version, then choose to upload it back to Immich or discard it completely. For a concise run-through of the workflow, see docs/how-it-works.md.
⚖️ License: Source-available under the PolyForm Noncommercial License 1.0.0. Free for personal and non-commercial use.
DailyFX has over 40 built-in creative modules and Generative AI effects to transform your photos.
👉 See the full list of available effects and AI modules in docs/effects.md
Workflow:
- Scheduler picks a weighted random effect and fetches photos from your Immich library
- Generated image appears in the web UI for review
- Accept → uploads to Immich with original EXIF (date, camera, GPS, etc.) and adds to album
- Reject → logged in history, not uploaded
CLI handoff:
- Run scheduled generation on the host using
./dailyfx-agent --schedule-id 1 --target agy. - For the full orchestrator CLI reference, including daemon mode, custom templates, vision pipelines, and troubleshooting, see the dailyfx-agent documentation.
Other features:
- Push notifications (Web Push, ntfy, Gotify, Telegram Bot, Home Assistant, Slack, Discord, Apprise) with image preview
- Mobile-friendly review page linked from notification
- Per-effect enable/disable and weight
- Filter by Immich album, person, date range, media type
- Schedules: daily, specific days, time of day
- Optional AI vision analysis (via OpenAI, Gemini, OpenRouter, or Xiaomi) for titles and captions; for
ai_*effects, the final generated image is analyzed again so the stored title, summary, and tags match the result you actually review. Seedocs/api.mdfor the metadata pipeline, includingmetadata_provenance,people_context,task_trace, and API Rate Limits. For API-key setup links, see docs/api.md.
Desktop
- Docker and Docker Compose
- A running Immich instance
There are two ways to run DailyFX: using pre-built images from GitHub Container Registry (recommended), or building from source.
You do not need to clone the repository to run DailyFX. You can run it directly using Docker Compose:
- Create a
docker-compose.ymlfile in an empty directory. You can choose either a minimal version or a production-ready version with healthchecks and limits:
services:
api:
image: ghcr.io/ferges123/dailyfx-api:latest
restart: unless-stopped
user: "${UID:-1000}:${GID:-1000}"
env_file: .env
volumes:
- ./data:/data
ports:
- "127.0.0.1:8438:8438"
web:
image: ghcr.io/ferges123/dailyfx-web:latest
restart: unless-stopped
ports:
- "127.0.0.1:8439:8080"services:
api:
image: ghcr.io/ferges123/dailyfx-api:latest
restart: unless-stopped
user: "${UID:-1000}:${GID:-1000}"
env_file: .env
volumes:
- ./data:/data
ports:
- "127.0.0.1:8438:8438"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mem_limit: 2g
cpus: 2.0
healthcheck:
test: ["CMD", "python", "healthcheck.py"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
web:
image: ghcr.io/ferges123/dailyfx-web:latest
restart: unless-stopped
ports:
- "127.0.0.1:8439:8080"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mem_limit: 512m
cpus: 1.0
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
depends_on:
api:
condition: service_healthy- Create a
.envfile next to yourdocker-compose.ymland add a secure random key:
APP_SECRET_KEY= # generate with: openssl rand -hex 32- Start the containers:
docker compose up -dIf you want to clone the repository and build the images locally:
- Clone the repository and copy
.env.example:
git clone https://github.com/ferges123/dailyFX.git
cd dailyFX
cp .env.example .env- Edit
.envto generate a secure secret key:
APP_SECRET_KEY= # generate with: openssl rand -hex 32- Build and start:
docker compose up --build -dOpen http://localhost:8439 in your browser. Go to the Settings tab to configure your Immich URL and API Key, plus any optional AI API keys (OpenAI, Gemini, OpenRouter, BytePlus for image generation, or Xiaomi).
If you want the simplest walkthrough, start with docs/getting-started.md. For the short operator flow summary, see docs/how-it-works.md. If you want the full deployment guide, see docs/self-hosting.md.
Use the repository Makefile for backend commands so you do not depend on global python or pytest being on your PATH:
make backend-install
make backend-testbackend-test runs python3 -m pytest inside backend/.
All connections (Immich, AI keys) and preferences are configured directly via the web UI (Settings and Presets tabs). The .env file is only used for system-level configuration at startup.
| Variable | Required | Description |
|---|---|---|
APP_SECRET_KEY |
yes | Random secret used to encrypt stored API keys. Generate with openssl rand -hex 32. Must not be the default placeholder value in production. |
APP_ENV |
no | Application environment (production or development, default: development). |
APP_HOST |
no | Host binding address (default: 0.0.0.0). |
APP_PORT |
no | Backend API port (default: 8438). |
DATA_DIR |
no | Directory for SQLite DB, results, and caches (default: ./data). |
DATABASE_URL |
no | Database connection string (default: sqlite:///./data/app.db). |
APP_ACCESS_TOKEN |
no | If set, all API endpoints require Authorization: Bearer <token>. Recommended for internet-exposed instances. |
REQUIRE_AUTH_FOR_REVIEW |
no | If set to true, history review and image endpoints will also require auth (default: false). |
APP_EXTERNAL_URL |
no | Public/External URL of your DailyFX instance (required for images and action buttons in notifications). |
APP_CONTACT_EMAIL |
no | Email to contact the administrator (default: dailyfx@localhost). |
EXAMPLE_ASSET_ID |
no | Any asset ID from your library — enables effect preview images in the Effects tab. |
CORS_ORIGINS |
no | Comma-separated allowed origins (default: localhost and frontend ports). |
LOG_JSON |
no | If set to true, use structured JSON logging instead of plain text (default: false). |
CONCURRENCY_LIMIT |
no | Maximum simultaneous image-generation tasks (default: 1). Raise only after measuring peak memory for the effects and image sizes in use. |
IMMICH_THUMBNAIL_CACHE_TTL |
no | TTL for cached Immich asset thumbnails (default: 7d). Supports time units: d (days), h (hours), m (minutes), s (seconds), w (weeks). |
IMMICH_THUMBNAIL_CACHE_RETENTION |
no | Retention period for local thumbnail cleanup (default: 30d). Supports time units: d (days), h (hours), m (minutes), s (seconds), w (weeks). |
Note
Immich connection details (IMMICH_URL and IMMICH_API_KEY), AI API keys, and notification integrations are managed dynamically in the web UI settings/presets instead of the .env file.
| Port | Service |
|---|---|
8439 |
Web UI (nginx) |
8438 |
Backend API |
To change ports, edit docker-compose.yml.
The backend ships with interactive API documentation available at:
| Path | Description |
|---|---|
http://localhost:8438/docs |
Swagger UI — interactive API explorer with live requests |
http://localhost:8438/redoc |
ReDoc — clean, readable API reference |
http://localhost:8438/openapi.json |
OpenAPI 3.1 spec — machine-readable schema |
Note
When APP_ACCESS_TOKEN is set, Swagger UI and ReDoc require the token to explore authenticated endpoints. Enter it in the Authorize button on the Swagger page.
Set up schedules in the Schedules tab:
- Daily at a specific time
- Specific days of the week
- With optional source filters (album, people, date range)
The scheduler runs inside the backend container. If automation is disabled, you can still trigger a selected schedule manually with the Run now button in the Schedules tab.
Manual Run now requests are added to History immediately as Queued, then switch to Running when the worker starts processing them.
For a practical explanation of what happens after a schedule fires, see docs/how-it-works.md.
Notification channels can be configured in the Notifications tab (on desktop) or under Presets -> Notifications (on mobile).
Subscribe from the browser on the Settings or Notifications page. Notifications include a preview of the generated image with a direct link to the review page.
Set your ntfy server URL, topic name, and optional token. When APP_EXTERNAL_URL is configured, DailyFX also attaches the generated image and a click target back to the review page.
Set your Gotify server URL and app token.
Get interactive notifications with action buttons (Accept & Upload / Reject) directly on your phone. See the Telegram Setup Guide for details.
Send notifications to Home Assistant via REST API. Supports push notifications (via mobile app services) and persistent sidebar notifications. See the Home Assistant Setup Guide for details.
- Create
backend/app/services/generation/modules/myeffect.py:
from app.models.settings import SettingsModel
from app.services.generation.modules.base import GenerationResult
class MyEffectModule:
name = "my_effect"
label = "My Effect"
description = "Does something cool."
default_weight = 1
default_config = {}
config_schema = []
async def run(self, page_items, config, client, settings: SettingsModel) -> GenerationResult:
asset = page_items[0]
image_bytes, _ = await client.get_asset_thumbnail(asset.id, size="preview")
# ... process image_bytes ...
return GenerationResult(
title=f"My Effect: {asset.original_file_name}",
summary="Applied my custom effect.",
image_bytes=processed_bytes,
generation_type=self.name,
provider="local",
model="pil",
config={},
source_asset_ids=[asset.id],
)- Register it in
backend/app/services/generation/modules/__init__.py:
from app.services.generation.modules.myeffect import MyEffectModule
LOCAL_MODULE_CLASSES = [
...,
MyEffectModule,
]The effect will appear automatically in the Effects tab — no other changes needed.
All persistent data is stored in ./data/:
| Path | Contents |
|---|---|
data/app.db |
SQLite database (settings, presets, schedules, history) |
data/results/ |
Generated PNG files (deleted on reject, cleaned up after 7 days) |
data/generation-examples/ |
Cached effect preview images |
scripts/measure_dailyfx_preset.pyreproduces the request-timing test against the local API. Example:It prints a markdown table with request time, asset name, date, and people, plus mean/min/max timing at the end.python3 scripts/measure_dailyfx_preset.py --preset TestA --count 50
scripts/render_filters_showcase.pyrenders a showcase/contact sheet of all active filters and generation modules.
For a screen-by-screen overview of the UI, data flow, and browser state, see docs/frontend.md.
For the backend layering, generation pipeline, and history/review flow, see docs/architecture.md.
For a complete guide to running DailyFX on your own network, see docs/self-hosting.md.
No. AI keys are only needed for AI image effects. The rest of the app works with Immich alone.
They are stored in data/results/ until you accept or reject them.
Yes. That is the intended use case for self-hosting.
Back up the data/ directory, especially data/app.db.
backend/ FastAPI + SQLAlchemy + Alembic
app/
api/ Routes: settings, generation, health, push, debug, presets, schedules, metrics, ai-effects, studio, immich
immich/ Immich API client
notifications/ Web Push, ntfy, Gotify, Telegram, Home Assistant
providers/ Split channel integrations (telegram, home_assistant, slack, discord, etc.)
services/
generation/
engine.py Compatibility entry points
pipeline/ Decoupled staged pipeline package (planning, assets, execution, etc.)
vision/ Modular AI vision provider adapters (gemini, openai, openrouter, etc.)
config_validation.py API-boundary config schema validator
persistence.py Result persistence and history writes
output.py Notifications and webhooks
modules/ Effect plugin system
settings/ Settings response/connection helpers
notifications/ Notification preset test helpers
workers/
scheduler.py Automation loop
healthcheck.py Dedicated multi-check API & scheduler health status script
frontend/ React 19 + Vite + Tailwind CSS
src/
pages/ Modular route views:
History/, Login.tsx, Presets/, Schedules/, Settings/
api/ API client (typed)
components/ RouteErrorBoundary.tsx (graceful route failure recovery)
version.ts Single source of truth version metadata
DailyFX is designed to keep your private memories secure, running locally on your own hardware via Docker.
Depending on your configuration, certain features require sending images and metadata to external cloud-based AI providers:
- Local Processing: If you only use built-in classic modules (e.g., Collage, Filmstrip, Polaroid, Cyanotype, Instafilter, Glitch, etc.) and do not configure any cloud API keys, all image processing and curation runs 100% locally on your machine. No data leaves your network.
- AI Image Generation: If you enable AI-powered image generation effects:
- OpenAI / Gemini / OpenRouter: The selected source photo and prompts are transmitted to their respective servers to generate the styled image.
- BytePlus: Your source photo is sent to BytePlus (TikTok/ByteDance) APIs for image styling.
- Xiaomi: Your source photo is sent to Xiaomi Cloud APIs.
- AI Vision Analysis (Metadata Generation): If you enable automated captioning, title, or tag generation:
- OpenAI / Gemini / OpenRouter / Xiaomi: The final styled image is uploaded to their respective APIs so that the vision model can analyze what actually appears in it and generate descriptive metadata.
You are in full control of which providers are enabled. External API keys can be added, updated, or removed at any time in the Settings tab.
Source-available for non-commercial use under the PolyForm Noncommercial License 1.0.0.


