How to Deploy TaxHacker to Production: Complete Docker Self-Hosting Guide

Deploy TaxHacker to production using Docker Compose with two containers—a Next.js 15+ application and PostgreSQL 17—configured via environment variables parsed in lib/config.ts and exposed on port 7331.

TaxHacker is a self-hosted, AI-powered accounting application maintained in the vas3k/TaxHacker repository. When you deploy TaxHacker to production, you run a containerized stack orchestrated by Docker, with all sensitive configuration centralized and validated through a Zod schema in lib/config.ts.

Production Architecture Overview

TaxHacker runs as a multi-container system designed for reproducible deployments. The architecture consists of a Next.js application server and a PostgreSQL database, connected via Prisma ORM.

The Application Stack

The app container runs the Next.js 15+ server built from the multi-stage Dockerfile. The build process uses node:23-slim as a base, installs openssl (required for Prisma), and bundles runtime dependencies including ghostscript and graphicsmagick for PDF and image processing. The production stage copies only built artifacts and runs docker-entrypoint.sh, which executes database migrations before starting the server on port 7331.

The postgres container runs PostgreSQL 17 using the official image, persisting data to a Docker volume. You can replace this with an external database by modifying the DATABASE_URL environment variable.

Configuration Management

All production settings derive from environment variables defined in .env.example and strictly parsed by the Zod schema in lib/config.ts (lines 3–23). This file exports a type-safe config object used throughout the application for AI provider keys (OPENAI_API_KEY, GOOGLE_API_KEY, MISTRAL_API_KEY), authentication secrets (BETTER_AUTH_SECRET), and infrastructure paths (UPLOAD_PATH).

Deployment Methods

You can deploy TaxHacker using three approaches depending on your infrastructure requirements: quick start with pre-built images, building from source, or a hardened production configuration.

Method 1: Quick Start with Docker Compose

The fastest way to deploy TaxHacker to production uses the provided docker-compose.yml file, which pulls the pre-built image from GitHub Container Registry.


# Download the compose configuration

curl -O https://raw.githubusercontent.com/vas3k/TaxHacker/main/docker-compose.yml

# Create environment variables

cat > .env <<EOF
SELF_HOSTED_MODE=true
UPLOAD_PATH=./data/uploads
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/taxhacker
BETTER_AUTH_SECRET=$(openssl rand -base64 32)
OPENAI_API_KEY=sk-your-key-here
EOF

# Deploy the stack

docker compose up -d

This command starts both containers, binds the application to port 7331, and automatically initializes the database using the entrypoint script at docker-entrypoint.sh.

Method 2: Building from Source

For custom modifications or air-gapped environments, build the image locally from the Dockerfile:

git clone https://github.com/vas3k/TaxHacker.git
cd TaxHacker

docker build -t taxhacker:custom .

docker run -d \
  -p 7331:7331 \
  -e SELF_HOSTED_MODE=true \
  -e DATABASE_URL=postgresql://user:pass@host:5432/taxhacker \
  -e BETTER_AUTH_SECRET=$(openssl rand -base64 32) \
  --name taxhacker_app taxhacker:custom

The Dockerfile implements a multi-stage build that minimizes the final image size by separating the build environment (requiring openssl and dev dependencies) from the production runtime.

Method 3: Production-Grade Deployment

For public-facing deployments behind a reverse proxy, use docker-compose.production.yml:

curl -O https://raw.githubusercontent.com/vas3k/TaxHacker/main/docker-compose.production.yml

# Edit .env to set SELF_HOSTED_MODE=false and BASE_URL=https://your-domain.com

docker compose -f docker-compose.production.yml up -d

Key differences from the quick-start method:

  • SELF_HOSTED_MODE is set to false, disabling the auto-login shortcut for security
  • The container binds to 127.0.0.1:7331 only, requiring an external reverse proxy (Nginx, Traefik, or Caddy) for TLS termination
  • External environment files are strictly enforced via env_file: declarations

Database Initialization and Migrations

The Prisma ORM handles schema management through lib/db.ts, which exports a singleton client with connection pooling. Migrations run automatically when the container starts via docker-entrypoint.sh, which executes:

npx prisma migrate deploy

To manually trigger a migration in a running container:

docker exec -it taxhacker_app npx prisma migrate deploy

The schema definition in prisma/schema.prisma defines tables for users, transactions, uploaded files, and application settings, which are created automatically on first run.

Required Environment Configuration

Before deploying TaxHacker to production, populate your .env file based on .env.example with these critical variables:

  • BETTER_AUTH_SECRET: A random string of at least 16 characters for password-less authentication encryption
  • DATABASE_URL: PostgreSQL connection string (e.g., postgresql://postgres:postgres@postgres:5432/taxhacker)
  • UPLOAD_PATH: Absolute path for persistent file storage (e.g., /app/data/uploads)
  • OPENAI_API_KEY, GOOGLE_API_KEY, or MISTRAL_API_KEY: Required only if using AI-powered document extraction
  • RESEND_API_KEY and RESEND_FROM_EMAIL: Required for transactional email delivery in production
  • STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET: Required only if operating as a paid SaaS

These values are validated at runtime by the Zod schema in lib/config.ts, which throws explicit errors if required variables are missing or malformed.

Security and Operations Checklist

Complete these steps before exposing TaxHacker to public traffic:

  1. Generate a secure BETTER_AUTH_SECRET using openssl rand -base64 32
  2. Configure TLS termination at your reverse proxy; the container serves plain HTTP on port 7331
  3. Back up the PostgreSQL volume regularly (the pgdata volume in Docker Compose)
  4. Set appropriate AI rate limits in next.config.ts if exposing to multiple users
  5. Verify file upload permissions on the UPLOAD_PATH directory to prevent unauthorized access

Summary

  • Deploy TaxHacker to production using Docker Compose with the official ghcr.io/vas3k/taxhacker:latest image or a custom build from the Dockerfile
  • Run two containers: a Next.js 15+ app container and a PostgreSQL 17 postgres container
  • Configure via lib/config.ts: Validate all settings through environment variables using the .env.example template
  • Expose port 7331: Bind to localhost for production deployments behind a reverse proxy, or directly for private networks
  • Initialize automatically: Database migrations run via docker-entrypoint.sh on container startup
  • Enable AI features: Provide OpenAI, Gemini, or Mistral API keys for automatic document parsing

Frequently Asked Questions

What ports and protocols does TaxHacker require?

TaxHacker exposes port 7331 over HTTP. You must place it behind a reverse proxy such as Nginx or Traefik to handle TLS/HTTPS termination in production. PostgreSQL communicates over port 5432 internally within the Docker network.

Can I use an external database instead of the Docker PostgreSQL container?

Yes. Set DATABASE_URL to point to your external PostgreSQL 17 instance (e.g., AWS RDS or Supabase) and remove the postgres service from your docker-compose.yml. Ensure the database is accessible from the TaxHacker container and that credentials match the format postgresql://user:password@host:port/database.

How do I upgrade to a new version of TaxHacker?

Pull the latest image (docker pull ghcr.io/vas3k/taxhacker:latest) or rebuild your custom image from the updated repository. Stop the containers, run docker compose up -d to restart, and the docker-entrypoint.sh script will automatically apply any pending database migrations via Prisma before starting the application.

Is a GPU required for the AI document extraction features?

No. TaxHacker uses cloud-based AI providers (OpenAI, Google Gemini, or Mistral) via API calls defined in lib/config.ts. The server itself runs on standard CPU resources, though you must provide valid API keys for the respective providers to enable automatic receipt and invoice parsing.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →