Selfhosted Login not Working
Description
I am not able to login on my instance. It send the OTP but after putting it in, I'm just redirected to the login again.
Reproduction
Clean Deployment of :beta or :latest Docker Image, first login does not even work.
Additional Context
- Cap version: ▲ Next.js 15.5.4
- Operating system, version: Debian 13
- Device (optional): --
A summary of the changes CodeRabbit can apply:
Update packages/database/auth/auth-options.ts to set the NextAuth session cookie's secure flag dynamically from NEXTAUTH_URL and change sameSite to "lax" (plus explanatory comments), so self‑hosted HTTP Docker instances receive session cookies and stop infinite login redirects while preserving secure behavior on HTTPS production.
Add a new troubleshooting doc (.github/ISSUE_FIXES/selfhosted-login-fix.md) describing the login loop root cause and solution, and modify packages/database/auth/auth-options.ts to switch cookie sameSite from "none" to "lax", make the secure flag dynamic (secure: serverEnv().NEXTAUTH_URL.startsWith("https://")), and add explanatory comments so self-hosted HTTP deployments can set cookies while HTTPS production remains secure.
- [ ] ✅ Create PR with these edits
- [ ] 📋 Get copyable edits
▲ Next.js 15.5.4
- Local: http://localhost:3000
- Network: http://0.0.0.0:3000
✓ Starting...
Waiting 5 seconds to run migrations
✓ Ready in 809ms
🔍 DB migrations triggered
💿 Running DB migrations...
💿 Migrations run successfully!
[next-auth][warn][DEBUG_ENABLED]
https://next-auth.js.org/warnings#debug_enabled
timestamp=2025-11-04T20:20:49.388Z level=INFO fiber=#29 message="Using CAP_AWS_ACCESS_KEY and CAP_AWS_SECRET_KEY"
[next-auth][warn][DEBUG_ENABLED]
https://next-auth.js.org/warnings#debug_enabled
[next-auth][debug][adapter_getUserByEmail] { args: [ '[email protected]' ] }
sendVerificationRequest
{ identifier: '[email protected]', token: '776911' }
[next-auth][debug][adapter_createVerificationToken] {
������
[
{
identifier: '[email protected]',
token: 'fff65fa5cdf8c95b930da2f6710cdaa860c90450cc8537a823337d4e18c7f642',
expires: 2025-11-05T20:20:56.536Z
}
]
}
{
email: {
'$$typeof': Symbol(react.transitional.element),
type: {
'$$typeof': Symbol(react.forward_ref),
render: [Function (anonymous)],
displayName: 'Html'
},
key: null,
ref: null,
props: { children: [Array] }
}
}
[next-auth][debug][adapter_useVerificationToken] {
������
[
{
identifier: '[email protected]',
token: 'fff65fa5cdf8c95b930da2f6710cdaa860c90450cc8537a823337d4e18c7f642'
}
]
}
[next-auth][debug][adapter_getUserByEmail] { args: [ '[email protected]' ] }
[next-auth][debug][adapter_getUserByEmail] { args: [ '[email protected]' ] }
[next-auth][debug][adapter_updateUser] {
������
[
{ id: 'zqbscjec4v63avm', emailVerified: 2025-11-04T20:21:22.235Z }
]
}
I managed to look into this again and it seems that after the login it wants to forward me to http://192.168.200.200:3008/login-success which is not found (404).
I'm also facing the same bug @heig
hey there, I faced the same bug. Problem was the http. For the current image to work you will need to setup https. I also created two DNS entries. one for the main app and one for the s3 / minio. Here is my compose and caddyfile.
name: cap-so-docker-template
# THIS IS A TEMPLATE FOR DOCKER COMPOSE FILES
# IT IS NOT MEANT FOR PRODUCTION DEPLOYMENT WITHOUT MODIFICATIONS TO ENVIRONMENT VARIABLES
# IT IS MEANT FOR LOCAL EVALUATION AND DEVELOPMENT PURPOSES ONLY
services:
cap-web:
container_name: cap-web
image: ghcr.io/capsoftware/cap-web@sha256:c5ea3500fbea5b1f2baa362c67f6dcae752ee2e14eb927ef80bdb6bdc053c74f
platform: linux/amd64
restart: unless-stopped
depends_on:
ps-mysql:
condition: service_healthy
minio:
condition: service_started
environment:
DATABASE_URL: 'mysql://root:@ps-mysql:3306/planetscale?ssl={"rejectUnauthorized":false}'
WEB_URL: https://cap.xxx.com
NEXTAUTH_URL: https://cap.xxx.com
# CHANGE THESE TO YOUR OWN VALUES
DATABASE_ENCRYPTION_KEY: c7a1e98e1e5e4cec5a3fbf5eaf502d262fb99807ae2be8ee70537409e29cb6f9
NEXTAUTH_SECRET: c7a1e98e1e5e4cec5a3fbf5eaf502d262fb99807ae2be8ee70537409e29cb6f9
# CHANGE THESE TO MATCH YOUR MINIO or S3 STORAGE SETUP
CAP_AWS_ACCESS_KEY: capS3root
CAP_AWS_SECRET_KEY: capS3root
CAP_AWS_BUCKET: capso
CAP_AWS_REGION: us-east-1
S3_PUBLIC_ENDPOINT: https://s3.cap.xxx.com
S3_INTERNAL_ENDPOINT: http://minio:3902
expose:
- 3000
caddy:
container_name: cap-caddy
image: caddy:2
restart: unless-stopped
depends_on:
- cap-web
environment:
CADDY_DOMAIN: cap.xxxcom
CADDY_EMAIL: [email protected]
ports:
- 80:80
- 443:443
volumes:
- caddy_data:/data
- caddy_config:/config
- ./infra/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
ps-mysql:
container_name: cap-primary-db
image: mysql:8.0
restart: unless-stopped
environment:
# CHANGE THESE TO YOUR OWN VALUES - BE SURE TO SET A PASSWORD FOR THE ROOT USER
MYSQL_DATABASE: planetscale
MYSQL_ROOT_HOST: "%"
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
command:
[
"--max_connections=1000",
"--default-authentication-plugin=mysql_native_password",
]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 5s
retries: 10
interval: 3s
start_period: 30s
ports:
- "3306:3306"
volumes:
- ps-mysql:/var/lib/mysql
# Local S3 Strorage
minio:
container_name: cap-minio-storage
image: "minio/minio:latest"
restart: unless-stopped
ports:
- "3903:3903"
expose:
- 3902
environment:
# CHANGE THESE TO YOUR OWN VALUES
- MINIO_ROOT_USER=capS3root
- MINIO_ROOT_PASSWORD=capS3root
command: server /data --address ":3902" --console-address ":3903"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3902/minio/health/live"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
volumes:
- minio-data:/data
- minio-certs:/certs
minio-setup:
container_name: cap-minio-setup
image: minio/mc:latest
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
mc alias set myminio http://minio:3902 capS3root capS3root;
mc mb myminio/capso --ignore-existing;
mc anonymous set public myminio/capso;
exit 0;
"
restart: "no"
volumes:
ps-mysql:
# REMOVE THESE IF YOU ARE NOT USING MINIO VIA THIS DOCKER COMPOSE FILE
minio-data:
minio-certs:
caddy_data:
caddy_config:
Caddyfile:
{
email {$CADDY_EMAIL}
}
{$CADDY_DOMAIN} {
encode gzip
reverse_proxy cap-web:3000 {
lb_policy round_robin
}
}
s3.{$CADDY_DOMAIN} {
encode gzip
reverse_proxy minio:3902 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
Hope this helps, it's a cool app, but the support for the true self hosting is still a little lackluster.
If you guys are still facing the issue, I was able to resolve this issue and have create a guide guide for community as well. @heig
Here: https://github.com/FarhanSE/cap-digital-ocean-deployment
If anyone still facing any issue, please reach out to me would love to help.
Thank you @FarhanSE ! I ported that you my envionment and it works great
Glad to know that. Welcome @heig
@FarhanSE do you know why after starting all containers and running the:
docker compose run --rm certbot certonly --webroot \
--webroot-path=/var/www/certbot \
--email [email protected] \
--agree-tos \
-d sub.domain.com
its stays on:
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
until I CTRL+C ? (domain up and running the capso already on http)
Wait for the process to complete, this is normal. Certbot is waiting for Let's Encrypt to verify domain ownership via HTTP-01 challenge. The process should finish automatically if:
-
Your domain DNS points to your server's IP
-
Port 80 is accessible from the internet
If it hangs for more than 2-3 minutes, check the logs at /var/log/letsencrypt/letsencrypt.log for specific errors. @alexey
Thanks again @FarhanSE !
For me its worked whei I removed HTTPS from nginx, left only:
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
in HTTP, then started nginx, requested cert with above command, then bring back HTTPS and restarted nginx, then everything worked