This page covers the issues most commonly reported by self-hosters. Each section includes the symptom, root cause, and the exact commands or configuration changes needed to resolve it. If your problem is not listed here, check the GitHub Issues tracker or ask in Discord.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ptshen/timeful-plus/llms.txt
Use this file to discover all available pages before exploring further.
Application won't start
Application won't start
Symptom: Look for lines like If the key is missing or empty, generate one and restart:Step 3 — Check MongoDB health:The status should show Step 4 — Confirm port 3002 is not already in use on the host:If something else is bound to 3002, either stop that process or change the host port in
docker compose up -d finishes but the app is unreachable at http://localhost:3002, or containers keep restarting.Step 1 — Read the logs:panic, dial tcp, no such host, or permission denied.Step 2 — Confirm the .env file exists and has a value for ENCRYPTION_KEY:healthy. If it shows starting or unhealthy, MongoDB is still initialising or has crashed. Wait 30 seconds and check again:docker-compose.yml (see the “Port conflicts” section below).Google OAuth errors
Google OAuth errors
Symptom: Clicking Sign in with Google shows an error page from Google, such as “redirect_uri_mismatch” or “Error 400: redirect_uri_mismatch”.Root cause: The redirect URI that Timeful sends to Google does not match any URI in your OAuth client’s Authorized redirect URIs list.Fix:
- Open Google Cloud Console → APIs & Services → Credentials.
- Click on your OAuth 2.0 Client ID.
- Under Authorized redirect URIs, confirm the following URI is listed exactly (including protocol, no trailing slash):
- Docker:
http://localhost:3002/auth - Production:
https://yourdomain.com/auth - Vercel deployment:
https://your-vercel-url.vercel.app/auth
- Docker:
- Also confirm Authorized JavaScript origins contains your frontend origin.
- Click Save and wait 1–2 minutes for changes to propagate.
- APIs not enabled — Make sure Google Calendar API and Google People API are enabled in your project under APIs & Services → Library.
- Wrong credentials — Confirm
CLIENT_IDin.env(or Railway Variables) matches the Client ID on the credentials page, not the project number or any other ID. - Credential not yet set in
config.js— ThegoogleClientIdinfrontend/public/config.jsmust matchCLIENT_ID. A mismatch causes the sign-in prompt to use the wrong client.
Port conflicts on 3002
Port conflicts on 3002
Symptom: After editing, restart the stack:The application is then available at
docker compose up fails with Bind for 0.0.0.0:3002 failed: port is already allocated.Fix: Change the host-side port in docker-compose.yml. The container-internal port (3002) must stay the same; only the left-hand side of the mapping changes:http://localhost:8080. Remember to update your Google OAuth Authorized redirect URIs and Authorized JavaScript origins if you change the port.MongoDB connection issues
MongoDB connection issues
Symptom: Backend logs show If MongoDB is still starting, wait 20–30 seconds and try again.Step 2 — Verify the connection string:Step 3 — For MongoDB Atlas: confirm IP access list:If ping fails, the containers are not on the same Docker network. Confirm
server selection error, connection refused, or dial tcp: lookup mongodb: no such host.Step 1 — Check MongoDB container logs:- Docker Compose (internal networking):
mongodb://mongodb:27017— the hostnamemongodbresolves to the container. - External Atlas:
mongodb+srv://user:pass@cluster.xxxxx.mongodb.net/...— verify user, password, and cluster address are correct.
- Go to Atlas → Network Access.
- Confirm
0.0.0.0/0is listed (or the specific IP of your Railway/Modal server). - If the IP is missing, click Add IP Address and add it.
docker-compose.yml puts both services on timeful-network.CORS errors in the browser
CORS errors in the browser
Symptom: The browser console shows For multiple origins (e.g., custom domain plus Vercel preview), use a comma-separated list with no spaces:After updating, restart the backend:
Access to fetch at '...' from origin '...' has been blocked by CORS policy.Root cause: The backend’s CORS_ALLOWED_ORIGINS variable does not include the exact origin the browser is sending.Fix:Set CORS_ALLOWED_ORIGINS to your frontend’s exact origin — same protocol, same domain, no trailing slash, no path:Login doesn't persist after OAuth
Login doesn't persist after OAuth
Symptom: Sign-in with Google completes successfully, but refreshing the page shows you as logged out.Root cause: Timeful uses session cookies. When the frontend and backend are on different domains (common in Vercel + Railway deployments), browsers block cross-site cookies by default unless they are marked Both values must be the frontend URL, not the backend URL. Redeploy after changing.
SameSite=None; Secure.This works correctly when both BASE_URL and CORS_ALLOWED_ORIGINS are set to the frontend’s https:// URL. The most common mistakes are:BASE_URLis set to the Railway backend URL instead of the Vercel frontend URL.BASE_URLuseshttp://instead ofhttps://(cookies require Secure flag over HTTPS).CORS_ALLOWED_ORIGINSdoes not match the actual origin the browser sends.
This issue does not affect Docker Compose deployments where the frontend and backend share the same origin (
http://localhost:3002).Outlook calendar integration not working
Outlook calendar integration not working
Symptom: Clicking Connect Outlook Calendar shows an error, or the button is greyed out with a message about Microsoft OAuth not being configured.Step 1 — Confirm If the variable is missing, you need to create an Azure App Registration:Step 3 — Check the redirect URI in Azure:The redirect URI in your Azure App Registration must exactly match your frontend domain plus
MICROSOFT_CLIENT_ID is set:- Go to portal.azure.com → Azure Active Directory → App registrations → New registration.
- Supported account types: Accounts in any organizational directory and personal Microsoft accounts.
- Redirect URI (Web):
https://yourdomain.com/auth(must match exactly). - After registration, copy the Application (client) ID → set as
MICROSOFT_CLIENT_ID. - Certificates & secrets → New client secret → copy the value → set as
MICROSOFT_CLIENT_SECRET. - API permissions → Add a permission → Microsoft Graph → Delegated permissions → add
offline_access,User.Read,Calendars.Read.
config.js:The microsoftClientId in frontend/public/config.js must match MICROSOFT_CLIENT_ID:/auth:- Docker:
http://localhost:3002/auth - Production:
https://yourdomain.com/auth
Getting Help
If none of the above resolves your issue, collect the following before asking for help:GitHub Issues
Open a bug report or search existing issues. Include your OS, Docker version, and the relevant log output.
Discord
Ask questions and get help from the Timeful community in real time.