🐳 TaxHacker

Self-hosted AI accounting app. LLM analyzer for receipts, invoices, transactions with custom prompts and categories

Docker Ready 3 Services Ports: 7331, 5432 5,577 stars
1. Pull Image docker pull library/taxhacker
2. 1-Click Launch Command docker run -d -p 7331:7331 --name taxhacker taxhacker:local
3. Compose Deployment docker compose up -d
docker-compose.build.yml
Download .yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: taxhacker:local
    ports:
      - "127.0.0.1:7331:7331"
    environment:
      - NODE_ENV=production
      - SELF_HOSTED_MODE=true
      - UPLOAD_PATH=/app/data/uploads
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-taxhacker}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-}
    volumes:
      - ./data:/app/data
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"

  cron:
    image: taxhacker:local
    environment:
      - NODE_ENV=production
      - SELF_HOSTED_MODE=true
      - UPLOAD_PATH=/app/data/uploads
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-taxhacker}
      # Optional override. When unset, both containers use the same persisted secret from ./data.
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-}
    volumes:
      - ./data:/app/data
      - ./etc/crontab:/mnt/crontab:ro
    restart: unless-stopped
    depends_on:
      - postgres
      - app
    security_opt:
      - no-new-privileges:true
    command: ["docker-cron-entrypoint.sh"]
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"

  postgres:
    image: postgres:17-alpine
    container_name: taxhacker-postgres
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in your .env file}
      - POSTGRES_DB=${POSTGRES_DB:-taxhacker}
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    ports:
      - "127.0.0.1:5432:5432"
    security_opt:
      - no-new-privileges:true
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"