Coworkee

Coworkee

Docker app from Coworkee's Repository

Overview

Coworkee is a modern, self-hosted HR platform for a single company (single-tenant), in the spirit of Personio / HR-Works. Fully bilingual (English and German). Modules: employees + org chart, absence/leave with approval workflow, time tracking, secure document storage, onboarding checklists, performance reviews and goals, an HR analytics dashboard, recruiting pipeline and a news feed. Role-based access control (ADMIN / HR / MANAGER / EMPLOYEE), light/dark mode and an audit log are built in. IMPORTANT - Coworkee needs a PostgreSQL 16 database. This template only starts the app container. Before starting it, run a PostgreSQL 16 container (e.g. the "postgres" template from Community Applications) with a database and user, then point DATABASE_URL below at it, for example: postgresql://coworkee:YOURPASSWORD@POSTGRES_HOST:5432/coworkee?schema=public Set AUTH_SECRET to a random string (e.g. `openssl rand -base64 32`). Leave DEMO empty for a fresh install - the first visit shows a setup wizard to create the admin account. Set DEMO to 1 only if you want the app to seed demo data and show demo logins. Uploaded documents are stored in /app/storage - keep the mapped appdata path so they survive updates.

Coworkee

Read this in Deutsch 🇩🇪.

QuickRun

Modern, self-hosted HR / personnel-management software in the spirit of Personio / HR-Works — for one company (single-tenant). Fully available in German and English, and extensible.

"A workplace for everything human."

Public landing page at /, the application after login at /dashboard.

Modules

Area Features
Employees Directory (search/filter/pagination), detail profiles, create/edit, org chart
Absence Leave balance, request → approval workflow, team overview, entitlement management
Time tracking Clock-in/out, weekly overview + hours, manual entries, team times
Documents Secure private storage, access-guarded download, HR upload, profile tab
Onboarding Checklist templates + per-employee processes with checkable tasks
Performance Goals (with self-service progress) + performance reviews (Draft → Submitted → Acknowledged)
Analytics HR dashboard with KPIs + charts (headcount, contract types, new hires, absence days)
Recruiting Job postings + applicant pipeline (Kanban, 6 stages)
News Announcement feed + in-app notifications (topbar bell)

Cross-cutting: roles & permissions (ADMIN / HR / MANAGER / EMPLOYEE, enforced server-side), DE/EN i18n (cookie-based, stored per user), light/dark mode, theming (style presets + corporate identity: accent color & logo), a REST API + MCP server (per-user tokens), and an audit log for write operations.

Tech stack

  • Next.js 16 (App Router, TypeScript, Turbopack)
  • PostgreSQL + Prisma 7 (pg driver adapter)
  • Auth.js v5 (NextAuth) — Credentials, JWT session
  • next-intl (DE/EN, no URL prefixes)
  • Tailwind CSS v4 + shadcn/ui (Base UI)
  • Vitest (unit tests)

Requirements

  • Node.js 20+
  • Docker (for local PostgreSQL) — or your own PostgreSQL instance

Quick start (local)

# 1. Dependencies
npm install

# 2. Environment variables
cp .env.example .env        # adjust DATABASE_URL + AUTH_SECRET if needed

# 3. Start PostgreSQL (Docker, host port 5433)
docker compose up -d

# 4. Migrate schema + load demo data
npx prisma migrate dev
npm run db:seed

# 5. Dev server
npm run dev

App: http://localhost:3000

Demo logins

All passwords: coworkee

Role Email
Administrator admin@coworkee.test
HR hr@coworkee.test
Manager manager@coworkee.test
Employee employee@coworkee.test

The logins are shown on the login page (click fills the form) when the instance runs in demo mode (DEMO=1).

Environment variables

Variable Purpose
DATABASE_URL PostgreSQL connection string (Prisma)
AUTH_SECRET Signing key for Auth.js sessions (required in production: openssl rand -base64 32)
DEMO 1 = demo instance: auto-seeds demo data on first boot if the DB is empty and shows the demo logins on /login. Unset/0 = real install (empty DB → setup wizard). Read at runtime.

The DB URL is read by Prisma 7 from prisma.config.ts (via dotenv); the app/auth read .env directly.

Scripts

npm run dev        # dev server
npm run build      # production build
npm run start      # production server (after build)
npm test           # Vitest
npm run db:migrate # prisma migrate dev
npm run db:seed    # demo data (idempotent)

Project structure (excerpt)

app/
  page.tsx              # public landing page (/)
  (auth)/login/         # login (split-screen + demo logins)
  (auth)/setup/         # first-run setup wizard (empty DB)
  (app)/                # protected app (sidebar shell)
    dashboard/ employees/ org/ absences/ time/ documents/
    onboarding/ performance/ analytics/ recruiting/ news/
    settings/ account/ notifications/
  actions/              # server actions (Zod-validated, audited)
  api/
    auth/[...nextauth]/ # Auth.js
    v1/                 # REST API (Bearer token, OpenAPI)
    mcp/                # MCP server (RBAC-scoped)
    documents/[id]/     # access-guarded file download
    branding/logo/      # tenant logo
    version/            # version endpoint
components/             # UI (shadcn), feature components, marketing
lib/                    # db, rbac, auth helpers, domain logic (tested)
messages/               # de.json, en.json (i18n)
prisma/                 # schema.prisma, migrations, seed.ts
deploy/                 # self-host / GHCR / Proxmox / Unraid / Umbrel packaging
storage/                # uploaded documents (not in repo, not public)

API & MCP

Coworkee exposes a real REST API and an MCP server, both authenticated with per-user API tokens that carry exactly that user's RBAC permissions (an AI/client can only do what the user may do).

  • Create/revoke tokens: Account page (self-service, shown once).
  • REST base: GET /api/v1/*me, employees, absences, time, documents, goals, reviews, recruiting, announcements, … · OpenAPI 3.1 at GET /api/v1/openapi.json.
  • MCP endpoint: POST /api/mcp (JSON-RPC, RBAC-scoped tools).
  • In-app reference + client config: /settings/api.

Send the token as Authorization: Bearer <token>.

Production notes

  • Documents are stored under storage/documents/ → hosting needs persistent storage (a volume). Serverless platforms without a persistent disk are unsuitable without object-storage integration.
  • Set AUTH_SECRET and a strong DB password in production. POSTGRES_PASSWORD must be URL-safe (use hex — no / + =), since it is embedded in DATABASE_URL.
  • German deployment reference: docs/DEPLOYMENT.md.

Installation & self-hosting

Coworkee ships as a Docker image (ghcr.io/fgilde/coworkeenextjs:latest) with a PostgreSQL 16 database. It listens on port 3000 and stores uploads in /app/storage (needs a persistent volume).

Fresh install vs. demo: on an empty database the first visit opens a setup wizard to create the admin account (no sample data). Set DEMO=1 to instead seed demo data and show the demo logins on /login (only on an empty DB; it never overwrites existing data). DEMO is read at runtime — no rebuild needed.

The GHCR image is published by CI on every push to master (tag latest) and on v* tags (semver). The GHCR package must be public (GitHub → Packages → the package → Package settings → Change visibility → Public) so servers can pull anonymously; otherwise run docker login ghcr.io (PAT with read:packages) first.

1. One command (any Linux with Docker)

curl -fsSL https://raw.githubusercontent.com/fgilde/CoworkeeNextJs/master/install.sh | sudo bash

Installs Docker if missing, clones to /opt/coworkee, generates secrets, asks for an optional domain (blank = plain HTTP on port 3000), and starts the stack with an empty database → setup wizard.

2. Docker Compose — prebuilt image (GHCR)

Recommended for low-RAM hosts (~1 GB): it pulls the prebuilt image instead of running next build, so it never OOMs. Run from the repo root:

# create deploy/.env with your secrets
cat > deploy/.env <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 24)
AUTH_SECRET=$(openssl rand -hex 32)
DEMO=0            # 0 = fresh install (setup wizard); 1 = seed demo data
EOF

docker compose --env-file deploy/.env -f deploy/docker-compose.ghcr.yml pull
docker compose --env-file deploy/.env -f deploy/docker-compose.ghcr.yml up -d

This stack includes a Caddy reverse proxy (automatic HTTPS). Edit the root Caddyfile to your own domain first, or for LAN/plain-HTTP use deploy/docker-compose.selfhost-http.yml (app published directly on port 3000).

3. Proxmox VE

On the Proxmox host shell (not inside a container):

bash -c "$(curl -fsSL https://raw.githubusercontent.com/fgilde/CoworkeeNextJs/master/deploy/proxmox/coworkee.sh)"

Creates an unprivileged Debian LXC (defaults: 2 vCPU, 4 GB RAM, 12 GB disk), installs PostgreSQL and Node in it, builds Coworkee from its newest tag, generates the database password and the session secret, and leaves a systemd service behind — then prints the container IP and URL (http://<ip>:3020). Override defaults via env, e.g. CORES=4 RAM_MB=8192 STORAGE=local-lvm bash …. Scripts: deploy/proxmox/coworkee.sh on the host, deploy/proxmox/install.sh inside — the second one works on any Debian machine and is also how you update: run it again.

No Docker in there, deliberately. On a current Proxmox an unprivileged container runs no Docker container at all — runc writes net.ipv4.ip_unprivileged_port_start and /proc/sys is read-only — and a privileged container buys that back by handing the container root on the host. The database, the generated secrets and the uploaded documents survive an update.

4. Unraid

Coworkee needs a separate PostgreSQL 16 container (Unraid Community Applications is per-container). First start a Postgres 16 container (e.g. the postgres CA template) with a coworkee database and user, then add the Coworkee app:

  1. Copy templates/coworkee.xml to /boot/config/plugins/dockerMan/templates-user/ on your Unraid server (or add its raw GitHub URL as a private CA template repo under Apps → Settings).
  2. In the template set DATABASE_URL to your Postgres container, e.g. postgresql://coworkee:PASSWORD@POSTGRES_HOST:5432/coworkee?schema=public, set AUTH_SECRET (openssl rand -base64 32), map /app/storage to appdata, and leave DEMO empty for a fresh install.
  3. Open the WebUI at http://<unraid-ip>:3000.

Official listing in Community Applications later requires publishing the template to a GitHub template repo and contacting the CA maintainer (Squid) on the Unraid forums.

5. Umbrel

This repository is itself a community app store: umbrel-app-store.yml names it and fgilde-coworkee/ is the app (umbrel-app.yml + docker-compose.yml, bundling the app + PostgreSQL 16). In Umbrel, App Store → ⋯ → Community app stores, add https://github.com/fgilde/CoworkeeNextJs.

CasaOS

App Store → Add source with https://github.com/fgilde/CoworkeeNextJs/releases/download/store/casaos-appstore.zip, rebuilt from store/casaos/ on every push.

Cosmos

store/cosmos/servapps/Coworkee/ is a ServApp with its own PostgreSQL; the installer form asks for the session secret.

For an official listing, open a PR against getumbrel/umbrel-apps adding a coworkee/ directory with these two files plus gallery images.

License

Coworkee is dual-licensed: the open-source GNU AGPLv3 (see LICENSE) or a commercial license for closed-source/proprietary use. Details and contact in LICENSING.md.

Proprietary – © 2026 Coworkee.

Install Coworkee on Unraid in a few clicks.

Find Coworkee in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Coworkee Review the template variables and paths Click Install

Related apps

Details

Repository
ghcr.io/fgilde/coworkeenextjs:latest
Last Updated2026-09-21
First Seen2026-09-21

Runtime arguments

Web UI
http://[IP]:[PORT:3000]
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Container Port: 3000 (Coworkee web UI)

Target
3000
Default
3000
Value
3000
DATABASE_URLVariable

PostgreSQL 16 connection string. Point this at your separate Postgres container.

Default
postgresql://coworkee:CHANGEME@POSTGRES_HOST:5432/coworkee?schema=public
AUTH_SECRETVariable

Auth.js session signing key. Generate with: openssl rand -base64 32

AUTH_TRUST_HOSTVariable

Let Auth.js trust the reverse-proxy host header. Keep 'true' on Unraid.

Default
true
Value
true
DEMO (optional)Variable

Leave empty for a fresh install (setup wizard). Set to 1 to seed demo data and show demo logins on an empty DB.

Target
DEMO
Storage (uploads)Pathrw

Uploaded documents. Must persist across container updates.

Target
/app/storage
Default
/mnt/user/appdata/coworkee/storage