Deploying a Huly App
Introduction
Huly is an open-source project management and issue-tracking platform built with a modern TypeScript/Node.js stack and PostgreSQL. Deploying Huly on Klutch.sh with a Dockerfile provides reproducible builds, managed secrets, and persistent storage for file uploads—all orchestrated from klutch.sh/app. This guide covers installation, repository setup, Dockerfile configuration, deployment steps, Nixpacks overrides, and production best practices.
Prerequisites
- A Klutch.sh account (create one)
- A GitHub repository containing your Huly code (GitHub is the only supported git source)
- Docker familiarity and Node.js 18+ installed locally for testing
- PostgreSQL connection details (host, port, database, user, password)
- Optional object storage credentials if you offload attachments
Recommended architecture and ports
- Serve the Huly web app over HTTP with an internal container port of
3000. - Run PostgreSQL as a separate Klutch.sh TCP app; expose it on port
8000and connect internally on port5432. - Use persistent storage for user uploads, exports, and cached assets (for example
/app/storage/uploads).
Repository layout
huly/├── apps/web/ # Next.js/React frontend├── apps/server/ # API/backend services├── packages/ # Shared libraries├── public/ # Static assets├── storage/ # Uploads and generated files (mount as a volume)├── Dockerfile # Must live at the repo root for Klutch.sh detection├── package.json├── pnpm-lock.yaml # or yarn.lock / package-lock.json└── .env.example # Template only; no secretsKeep secrets out of Git and rely on Klutch.sh environment variables.
Installation (local) and starter scripts
Install dependencies locally before pushing to GitHub:
pnpm installpnpm buildpnpm startIf your project uses migrations, add a script such as:
pnpm prisma migrate deployConsider a helper start.sh for portability and Nixpacks fallback:
#!/usr/bin/env bashset -euo pipefailpnpm prisma migrate deploypnpm start -- --port 3000Make it executable with chmod +x start.sh.
Dockerfile for Huly (production-ready)
Place this at the repository root so Klutch.sh auto-detects it; there is no Docker selection in the UI:
FROM node:18-alpine AS buildWORKDIR /app
# Install dependenciesCOPY package.json pnpm-lock.yaml* yarn.lock* package-lock.json* ./RUN corepack enable
# Use pnpm by default; adjust if you prefer npm or yarnRUN pnpm install --frozen-lockfile
# Copy source and buildCOPY . .RUN pnpm build
# Production imageFROM node:18-alpineWORKDIR /appENV NODE_ENV=production \ PORT=3000
# Copy built assetsCOPY --from=build /app /app
# Install only production dependenciesRUN corepack enable && pnpm install --prod --frozen-lockfile
EXPOSE 3000CMD ["pnpm", "start", "--", "--port", "3000"]Notes:
- If you compile native modules, install the required Alpine build tools in the build stage (
apk add --no-cache python3 make g++). - Keep the
storage/path writable for uploads; mount it as a volume in Klutch.sh.
Environment variables (Klutch.sh)
Set these in the Klutch.sh app settings (Secrets tab) before deploying:
NODE_ENV=productionPORT=3000APP_BASE_URL=https://example-app.klutch.shDATABASE_URL=postgresql://<user>:<password>@<host>:<port>/<db>JWT_SECRETor equivalent auth secretSTORAGE_DIR=/app/storage/uploads(or your chosen path)NEXT_PUBLIC_APP_URL=https://example-app.klutch.sh(for frontends)
If you customize Nixpacks (for times you deploy without the Dockerfile), use:
NIXPACKS_BUILD_CMD=pnpm install --frozen-lockfile && pnpm buildNIXPACKS_START_CMD=pnpm start -- --port 3000NIXPACKS_NODE_VERSION=18
These ensure Huly builds and serves correctly with Nixpacks defaults.
Attach persistent volumes
In Klutch.sh storage settings, add mount paths and sizes (names are not required):
/app/storage/uploads— for attachments and exported assets./app/.cache— optional cache to speed rebuilds if your app writes there.
Confirm the Node process can write to these directories inside the container.
Deploy Huly on Klutch.sh (Dockerfile workflow)
- Push your repository (with the Dockerfile at the root) to GitHub.
- Open klutch.sh/app, create a project, and add an app.
- Connect the GitHub repository; Klutch.sh automatically detects the Dockerfile.
- Choose HTTP traffic for the Huly web app.
- Set the internal port to
3000. - Add the environment variables listed above (including database and JWT secrets).
- Attach persistent volumes for
/app/storage/uploads(and/app/.cacheif used), selecting sizes that match your storage needs. - Deploy. Your app will be live at
https://example-app.klutch.sh; attach a custom domain if desired.
For PostgreSQL on Klutch.sh, create a separate TCP app, expose it on port 8000, and point DATABASE_URL to that endpoint (internal port 5432).
Health checks and production tips
- Add a lightweight health route (e.g.,
/api/health) for uptime monitoring. - Enable structured logging and route logs to your observability stack.
- Keep lockfiles committed and dependency versions pinned for reproducible builds.
- Monitor disk usage on mounted paths and resize volumes before they fill.
- Back up PostgreSQL regularly; do not rely on container filesystems for data durability.
Huly on Klutch.sh combines fast, reproducible Docker builds with managed secrets, persistent volumes, and flexible HTTP/TCP traffic options. With the Dockerfile at the repo root and ports set to 3000 for the app (8000 externally for TCP databases), you can ship a stable project management experience without extra YAML or workflow overhead.