turkushan-auth

turkushan-auth

Docker app from turkushan-auth's Repository

Overview

Self-hosted login portal (forward auth) for your subdomains behind Nginx Proxy Manager. People register their own account, you approve who may open which site in the admin panel, and one login works on all subdomains (SSO cookie). Per site you choose: approval required or open to any logged-in user, and whether a verified email is required. Single container, SQLite database in /data, no extra services. Includes an admin panel with a ready-to-paste NPM config per site, email verification and password reset via any SMTP provider (Resend recommended), and Discord notifications. Guide: https://github.com/turkushan490/turkushan-auth/blob/main/docs/GUIDE.md

turkushan-auth

A self-hosted login portal for your subdomains behind Nginx Proxy Manager (forward auth, like Tinyauth). People create their own account, you decide per site who gets in, and one login works on all your subdomains.

Everything runs in one container: a Go backend and a Svelte frontend in a single binary, with SQLite in /data. No database server, no Redis, no compose.

Sign-in page

Features

  • Sign in, create account, forgot password: dark, mobile-friendly pages
  • Continue with Discord or Google (optional)
  • Unlock sites by Discord server or role: everyone with a certain role in your Discord server gets into the sites you choose, automatically, without a bot
  • Single sign-on across *.your-domain with one session cookie
  • Per site: "needs approval" or "open for everyone signed in", and optionally "needs a verified email"
  • Back to where you were: after signing in, visitors land on the page they opened
  • Admin panel:
    • approve/deny requests
    • manage users (create or invite, access per site, block, unlock, reset password, delete), with search, filters and sorting
    • groups that give site access and panel rights, with auto-add rules; several admins
    • add sites with a ready-to-paste NPM config and a Copy button
    • audit log of every admin action
  • 25 languages with a searchable language picker. The portal follows the visitor's browser language, and mails go out in the language they chose. You can set a default and limit which languages are offered.
  • Your own look: logo, site name, accent color, font and background (image, color or gradient), set in the admin panel with a live preview
  • Email via any SMTP provider (Resend recommended): verify addresses, reset passwords
  • Discord notifications for new accounts and access requests
  • Security: argon2id, server-side sessions, CSRF protection, rate limits, lockout, open-redirect protection, strict security headers; see SECURITY.md

Screenshots

Users Users: who can open what, groups, login methods, search and filters Groups Groups: site access, panel rights and auto-add rules
Requests Requests: approve or deny Sites Sites: rules per site and the NPM config to paste
Appearance Appearance: logo, colors, font, background Settings Settings: email, Discord/Google login, notifications
Waiting for approval Waiting for approval First Discord sign-in First sign-in with Discord or Google
Email step Verified email needed Account Account page
Sign-in page on a phone

Example: unlock sites with a Discord role

Give everyone with a certain role in your Discord server access to a site, without approving them one by one:

  1. Turn on Discord login in Settings → Sign in with Discord or Google (how).
  2. Groups → New group, e.g. "Discord VIP". Under Sites, tick Can open for the sites the role unlocks.
  3. Under Auto-add rules, choose Discord role and paste your server ID and role ID (Discord → Settings → Advanced → Developer Mode, then right-click the server or role → Copy ID).

From then on, everyone with that role gets in right after signing in with Discord. Take the role away and they're out at their next Discord sign-in. Roles are read when someone signs in, so no bot is needed. Details: guide, step 8.4.

Get started

📖 Read the guide. It covers installing on Unraid, the NPM setup, protecting your first site, email with Resend, Discord, backups and troubleshooting.

Quick version:

  1. Install turkushan-auth from Community Apps, or via the template in templates/. Set APP_URL, ADMIN_USER and ADMIN_PASSWORD.
  2. In NPM, add a proxy host auth.your-domain → <server-ip>:3010 with SSL, and Block Common Exploits off.
  3. Sign in at https://auth.your-domain → Admin panel → Sites → Add site.
  4. Copy the site's NPM config into the Advanced tab of that site's proxy host in NPM.

Settings

Variable Default
APP_URL required public portal URL, e.g. https://auth.example.com
ADMIN_USER / ADMIN_PASSWORD admin created on first start; the password is not overwritten on restart
COOKIE_DOMAIN derived from APP_URL auth.example.com gives .example.com, shared by all subdomains
TRUSTED_PROXIES 172.16.0.0/12 proxy addresses allowed to pass client IP headers
PORTAL_INTERNAL_URL how NPM reaches the portal; can also be set in the admin panel
SESSION_SECRET auto-generated in /data/secret
PUID / PGID 99 / 100 user the app runs as
PORT 3010 port inside the container
DATA_DIR /data where the database, secret and uploads live
LOG_LEVEL info debug, info, warn or error
LOG_FORMAT text text, or json for log collectors

Email, Discord and the portal address are set in Admin panel → Settings.

Images and versions

ghcr.io/turkushan490/turkushan-auth is published for amd64 and arm64.

Tag Follows
latest every change on main
1 everything up to the next big change
1.0 bug fixes only
1.0.0 stays on exactly that version

What changed in each version is in the changelog.

Backup and recovery

Back up the database while the portal runs, in one of these ways:

  • Admin panel → Settings → Download backup
  • docker exec turkushan-auth /turkushan-auth backup (writes to /data/backups/)

Locked out of the admin account without a verified email? This asks for the new password:

docker exec -it turkushan-auth /turkushan-auth reset-password <username>

Languages

The portal and its mails are available in English, Dutch, Turkish, German, French, Spanish, Portuguese, Italian, Polish, Russian, Ukrainian, Arabic, Persian, Hindi, Japanese, Korean, Chinese (Simplified and Traditional), Indonesian, Vietnamese, Thai, Swedish, Danish, Norwegian and Finnish. Arabic and Persian are shown right to left.

Which language a visitor sees:

  1. The language they picked in the picker. It's saved on their account, so their mails use it too.
  2. Otherwise, their browser's language.
  3. Otherwise, the default you set in Admin panel → Appearance → Language. You can also limit which languages are offered there.

The translations were made with machine help. Found a clumsy sentence? Corrections are very welcome as a pull request:

  • web texts: web/src/locales/<code>.json
  • mail texts: internal/i18n/mail/<code>.json

npm run check-locales in web/ checks that every language has the same keys and placeholders as English.

Development

GitHub Actions (.github/workflows/build.yml) does the following:

  • builds and checks the frontend;
  • runs gofmt, go vet, go test -race and govulncheck;
  • scans the image with Trivy;
  • pushes ghcr.io/turkushan490/turkushan-auth:latest (amd64 + arm64) on every push to main.

A v1.2.3 tag also pushes :1.2.3, :1.2 and :1, and creates a GitHub Release from that version's section in CHANGELOG.md. Dependabot keeps Go, npm, Actions and the Docker base images up to date.

cd web && npm install && npm run build && cd ..
APP_URL=http://localhost:3010 DATA_DIR=./data ADMIN_USER=admin ADMIN_PASSWORD='Change!me' go run ./cmd/turkushan-auth

For frontend work, npm run dev in web/ proxies /api to a portal running on port 3010.

License

MIT

Media gallery

1 / 5

Requirements

Nginx Proxy Manager (or nginx with auth_request) and a domain with HTTPS.

Related apps

Explore more like this

Explore all

Details

Repository
ghcr.io/turkushan490/turkushan-auth:latest
Last Updated2026-11-14
First Seen2026-09-30

Runtime arguments

Network
bridge
Privileged
false

Template configuration

Web portPorttcp

Port Nginx Proxy Manager uses to reach the portal.

Target
3010
Default
3010
Value
3010
DataPathrw

SQLite database and generated secret.

Target
/data
Default
/mnt/user/appdata/turkushan-auth
Value
/mnt/user/appdata/turkushan-auth
APP_URLVariable

Public URL of the portal, e.g. https://auth.example.com

ADMIN_USERVariable

Admin account created on first start.

ADMIN_PASSWORDVariable

Admin password, only needed on the first start (remove it afterwards): at least 6 characters, with 1 capital letter and 1 symbol (e.g. ! @ # ?).

COOKIE_DOMAINVariable

Optional. Cookie domain shared by all protected subdomains. Empty = derived from APP_URL (auth.example.com gives .example.com).

PORTAL_INTERNAL_URLVariable

Optional. How Nginx Proxy Manager reaches this container, e.g. http://192.168.1.10:3010. Filled into the nginx snippets the admin panel generates.

TRUSTED_PROXIESVariable

IPs/CIDRs of your proxy. Client IP headers are only trusted from these. Default covers Docker networks.

Default
172.16.0.0/12
Value
172.16.0.0/12
PUIDVariable

User the app runs as (99 = nobody).

Default
99
Value
99
PGIDVariable

Group the app runs as (100 = users).

Default
100
Value
100
LOG_LEVELVariable

Optional. debug, info, warn or error.

Default
info
Value
info
LOG_FORMATVariable

Optional. text, or json for log collectors.

Default
text
Value
text
SESSION_SECRETVariable

Optional (min 32 characters). Empty = generated once and stored in /data/secret.