# LibreLedger — the app plus its persistence server (server.py, standard # library only). The browser encrypts; the container only stores ciphertext. # # Build: docker build -t libreledger . # Run: docker run --rm -p 8080:8080 -v libreledger-data:/data libreledger # Open: http://localhost:8080 # # Base image pinned by version AND index digest (multi-arch). Bump both together. FROM python:3.14.7-alpine3.24@sha256:c6ead215bfd31f1e433d968853b7a769989117115b728874824e6c0a27cb96fc ARG VERSION=1.0.0 LABEL org.opencontainers.image.title="LibreLedger" \ org.opencontainers.image.description="Encrypted personal money ledger: your data is encrypted in your browser; the server never sees it." \ org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.source="https://git.libreportal.org/LibrePortal/LibreLedger" \ org.opencontainers.image.licenses="AGPL-3.0-or-later" ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ LIBRELEDGER_HOST=0.0.0.0 \ LIBRELEDGER_PORT=8080 \ LIBRELEDGER_DATA_DIR=/data # Unprivileged by default. LibrePortal may override the uid so the process # owns its bind mount (under rootless Docker that is container root, which is # the unprivileged install user on the host). RUN addgroup -S -g 10001 libreledger \ && adduser -S -D -H -u 10001 -G libreledger -s /sbin/nologin libreledger \ && mkdir -p /data \ && chown libreledger:libreledger /data \ && chmod 700 /data WORKDIR /app COPY --chmod=0444 server.py index.html app.js theme.js crypto-fallback.js styles.css favicon.svg ./ COPY --chmod=0444 banks/*.svg ./banks/ COPY --chmod=0444 vendor/ ./vendor/ RUN find /app -type d -exec chmod 0555 {} + USER libreledger EXPOSE 8080 VOLUME ["/data"] HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD ["python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8080/api/health', timeout=4).status == 200 else 1)"] CMD ["python3", "/app/server.py"]