Docker voor Finch
Finch is ontworpen om goed te draaien binnen Docker. De officiële uproid/finch-image bevat de Dart SDK, de finch-CLI en alles wat nodig is om een Finch-applicatie te bouwen en te serveren. Het example/-project in deze repository levert een volledige docker-compose.yaml die je kunt kopiëren als uitgangspunt voor MongoDB, MySQL, SQLite, een Nginx reverse proxy en het bouwen van Tailwind-assets — allemaal in één stack.
Projectstructuur
Een typisch Docker-gebaseerd Finch-project gebruikt deze bestanden:
my-app/
Dockerfile # Bouwt de Finch-containerimage
docker-compose.yaml # Orkestreert alle services
docker/
nginx.conf # Nginx reverse-proxyconfiguratie
lib/ # Applicatie Dart-code
public/ # Statische bestanden geserveerd door Nginx
sqlite/ # SQLite-databasebestanden (volume-mounted)
mongodb/ # MongoDB-databestanden (volume-mounted)
mysql/ # MySQL-databestanden (volume-mounted)
Dockerfile
De voorbeeld-Dockerfile gebruikt de officiële Finch-basisimage en installeert afhankelijkheden tijdens het bouwen:
FROM uproid/finch:latest AS build
WORKDIR /www
# Standaardwaarden voor paden/gedrag — overschrijf per omgeving in docker-compose.yaml
ENV WIDGETS_PATH=./lib/widgets
ENV WIDGETS_TYPE=j2.html
ENV LANGUAGE_PATH=./lib/languages
ENV PUBLIC_DIR=./public
ENV LOCAL_DEBUG=true
ENV ENABLE_DATABASE=true
COPY pubspec.yaml ./
RUN dart pub get --no-offline
COPY lib/ ./lib/
# Build: genereert de finch-appbinary en compileert routes/widgets vooraf
RUN finch -u
# App-poort (8085) en de Dart VM-service/DevTools-poort (8181)
EXPOSE 8085 8181
# Bij het starten: voer de app uit en pas openstaande MySQL- en SQLite-migraties toe
CMD ["finch", "serve", "-p", "/www/lib/watcher.dart", "--args=\"migrate --init --and migrate_sqlite --init\""]
finch -u (zie Finch CLI) heractiveert de globaal geïnstalleerde finch-CLI zelf — gelijk aan het opnieuw uitvoeren van dart pub global activate finch — zodat de build-/serve-tooling in de image overeenkomt met de laatst gepubliceerde Finch-release, in plaats van de versie die toevallig in de uproid/finch:latest-basisimage is ingebakken. De CMD voert de app uit via finch serve, houdt lib/watcher.dart in de gaten (zie Run App) en geeft migrate --init / migrate_sqlite --init door zodat databasemigraties (zie Database Migration) automatisch worden toegepast bij elke containerstart — veilig om herhaaldelijk uit te voeren, aangezien al toegepaste migraties worden overgeslagen.
Als je de local debugger (ENABLE_LOCAL_DEBUGGER=true) binnen een container inschakelt, voeg dan ook EXPOSE 8282 toe en publiceer deze in docker-compose.yaml — die poort draagt de interactieve terminal van de debugger via een WebSocket en wordt standaard niet blootgesteld.
Opmerking: de root-
Dockerfileendocker-compose-live.yamlin deze repository bouwen het Finch-framework zelf voor het eigen live voorbeeld en zijn niet bedoeld om te kopiëren naar een applicatieproject — begin altijd bijexample/Dockerfileenexample/docker-compose.yaml.
docker-compose.yaml
De volledige voorbeeldstack bestaat uit vijf services: finch (je app), nginx (reverse proxy), mongodb, mysql en nodejs (voor het bouwen van Tailwind CSS-assets):
services:
finch:
hostname: finch
container_name: finch
image: uproid/finch:latest
restart: always
ports:
- "8085:8085" # App-poort
- "8181:8181" # Dart VM-service / DevTools-poort
environment:
SQLITE_PATH: /www/sqlite/example_database.sqlite
ENABLE_DATABASE: true
MONGODB_CONNECTION: mongodb # Gebruik de servicehostnaam, niet localhost
MONGODB_PORT: 27017 # Interne containerpoort (niet de naar de host gemapte 27018)
MONGODB_NAME: my_database
MONGODB_AUTH: admin
MONGODB_PASSWORD: change-me
MONGODB_USER: root
MYSQL_HOST: mysql
MYSQL_PORT: 3306
MYSQL_DATABASE: my_database
MYSQL_USER: app_user
MYSQL_PASSWORD: change-me
build:
context: .
volumes:
- ./lib/:/www/lib/ # Hot-reload broncode zonder de image opnieuw te bouwen
- ./public/:/www/public/
- ./sqlite/:/www/sqlite/ # SQLite-data behouden over containerherstarts heen
nodejs:
image: node:20-alpine
container_name: nodejs
working_dir: /app
tty: true
stdin_open: true
volumes:
- ./:/app
command: sh -c "npm install && npm run tailwind:watch"
restart: always
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8080:8080" # Publiek toegankelijke poort
volumes:
- ./public/:/var/www/html/public
- ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
mongodb:
image: mongo:latest
container_name: mongodb
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: change-me
ports:
- "27018:27017" # Hostpoort 27018 om conflicten met een lokale MongoDB-installatie te voorkomen
volumes:
- ./mongodb:/data/db
mysql:
image: mysql:latest
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: change-me-root
MYSQL_DATABASE: my_database
MYSQL_USER: app_user
MYSQL_PASSWORD: change-me
ports:
- "3306:3306"
volumes:
- ./mysql:/var/lib/mysql
Belangrijke punten:
nodejsis alleen relevant als je project Tailwind gebruikt (zie depackage.jsonentailwind.config.jsvan het voorbeeld); laat deze service volledig weg als je geen frontend-assets bouwt.mongodbenmysqlzijn onafhankelijk van elkaar — schakel alleen de databases in die je app daadwerkelijk gebruikt doorenable: falsein te stellen op de bijbehorendeFinchDBConfig/FinchMysqlConfig/FinchSqliteConfigin jeconfigs.dart(zie Configuration), en verwijder hier simpelweg het ongebruikte serviceblok.- Binnen het Docker-netwerk bereiken containers elkaar via de servicenaam (
mongodb,mysql,nginx), nooit vialocalhost— daarom wijzenMONGODB_CONNECTIONenMYSQL_HOSTnaar de servicenamen en niet naarlocalhost, ook al zou hetzelfde.env-bestand dat wordt gebruikt voor lokale (niet-Docker) ontwikkelinglocalhostbevatten. - De naar de host gemapte MongoDB-poort (
27018) is anders dan de poort waarmee Finch intern verbinding maakt (27017) — de app praat met Mongo over het Docker-netwerk op de eigen poort, terwijl27018alleen bestaat zodat je vanaf je hostmachine een lokale MongoDB-client kunt verbinden metlocalhost:27018om te debuggen.
Nginx reverse proxy
docker/nginx.conf proxyt zowel reguliere HTTP-verzoeken als de WebSocket-gebaseerde /ws- en /debugger-endpoints (gebruikt door WebSockets en de local debugger) naar de finch-container:
upstream finch {
server finch:8085;
keepalive 32;
}
server {
listen 8080;
server_name localhost:8080;
client_max_body_size 20M;
location / {
root /var/www/html;
try_files /public$uri @dart;
}
# WebSocket-ondersteuning voor /ws- en /debugger-paden
location ~ ^/(ws|debugger) {
proxy_pass http://finch;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_buffering off;
proxy_read_timeout 3600s;
}
location @dart {
proxy_pass http://finch;
proxy_pass_request_body on;
proxy_pass_request_headers on;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
try_files /public$uri @dart betekent dat Nginx statische bestanden rechtstreeks serveert vanuit de gemounte public/-map als ze bestaan, en het verzoek alleen doorstuurt naar Finch (@dart) als dat niet zo is — dit is dezelfde Nginx for Finch-configuratie die buiten Docker wordt gebruikt, maar dan gericht op de finch-container in plaats van een lokaal proces. proxy_buffering off en de lange timeouts in het WebSocket location-blok zijn vereist — zonder deze zal Nginx langlopende WebSocket-verbindingen bufferen of voortijdig sluiten.
De stack starten
# Bouw en start alle services
docker-compose up --build
# Op de achtergrond uitvoeren (detached)
docker-compose up -d --build
# Logs van één service volgen
docker-compose logs -f finch
# Bouw alleen de finch-service opnieuw na een Dockerfile-wijziging
docker-compose up -d --build finch
# Stop alles (alleen containers — volumes blijven behouden)
docker-compose down
# Stop alles EN verwijder volumes (verwijdert alle databasedata)
docker-compose down -v
Zodra deze draait, is de app bereikbaar via http://localhost:8080 (via Nginx) of rechtstreeks via http://localhost:8085 (Nginx omzeilend, handig bij het debuggen of een probleem in Finch of in de proxyconfiguratie zit).
Omdat ./lib/ en ./public/ als bind-mount in de container zijn gekoppeld, worden wijzigingen aan die bestanden op de host onmiddellijk opgemerkt door Finch's file watcher — je hoeft de image niet opnieuw te bouwen voor dagelijkse Dart-codewijzigingen, alleen wanneer de afhankelijkheden in pubspec.yaml veranderen.
Omgevingsvariabelen
Elke FinchConfigs-waarde die wordt gelezen via env.get(...) (of env.getInt(...) / env.getBool(...)) kan worden ingesteld als Docker-omgevingsvariabele of via een .env-bestand dat door de app wordt geladen. Een minimale .env die overeenkomt met het bovenstaande compose-bestand:
MONGODB_CONNECTION=mongodb
MONGODB_PORT=27017
MONGODB_NAME=my_database
MONGODB_AUTH=admin
MONGODB_USER=root
MONGODB_PASSWORD=change-me
MYSQL_HOST=mysql
MYSQL_PORT=3306
MYSQL_DATABASE=my_database
MYSQL_USER=app_user
MYSQL_PASSWORD=change-me
SQLITE_PATH=/www/sqlite/example_database.sqlite
ENABLE_LOCAL_DEBUGGER=false
LOCAL_DEBUG=false
Zie Configuration voor de volledige lijst met ondersteunde omgevingsvariabelen, inclusief server-, sjabloon- en mailinstellingen die hier niet worden getoond.
Commit nooit echte wachtwoorden. Geef in docker-compose.yaml zelf de voorkeur aan ${VAR_NAME}-interpolatie vanuit een .env-bestand dat via .gitignore is uitgesloten, in plaats van geheimen rechtstreeks hard te coderen in de YAML zoals hierboven omwille van de beknoptheid is getoond.
Overwegingen voor productie
- Zet
ENABLE_LOCAL_DEBUGGER=falseenLOCAL_DEBUG=falsein productie — de local debugger stelt een WebSocket-terminal naar de draaiende container bloot en is alleen bedoeld voor ontwikkeling. Zie Debugging. - Publiceer de Dart VM-service-poort (
8181) niet buiten je eigen netwerk; deze geeft volledige lees-/schrijftoegang tot de draaiende isolate. - Plaats een echte TLS-terminerende reverse proxy (Nginx met een certificaat, of een managed load balancer) vóór de stack — de voorbeeld-
nginx.confhier is alleen HTTP en bedoeld als uitgangspunt. - Gebruik named volumes (of een managed database) voor
mongodb/mysql-data in productie in plaats van een host-directory te bind-mounten, zodat back-ups en rechten consistent worden afgehandeld over hosts heen. - Verhoog
maxConnectionsopFinchDBConfig/FinchMysqlConfigals je verder opschaalt dan een handvol gelijktijdige gebruikers — zie MongoDB en MySQL.