I share real-world lessons from building scalable systems at Jump Trading, Binance, and running mission-critical cloud ops at GovTech and Singapore Air Force. No fluff, just practical takeaways, hard-earned fixes, and deep dives that matter.
Env-only config vars are always read from environment variables
Database values (stored in core."keyValuePair" table) override environment variables
Environment variables are the fallback if no database entry exists
Database overrides env vars silently
If a config value exists in the core."keyValuePair" table, the environment variable in env_file or environment is completely ignored. This can cause confusing bugs where changing the .env file has no effect.
Check database config when env changes don't take effect
SELECT key, value FROM core."keyValuePair" WHERE key LIKE '%YOUR_CONFIG%';
Update with:
UPDATE core."keyValuePair" SET value='"new_value"' WHERE key='YOUR_CONFIG_KEY';
Note the double quoting: the value column stores JSON, so strings need '"value"'.
Both URIs must be registered in Google Cloud Console > APIs & Services > Credentials > OAuth 2.0 Client IDs > Authorized redirect URIs for the same client
Debugging the No Payload Error
Symptom: clicking “Sign in with Google” on Twenty’s login page produces a cryptic “No payload” error in the frontend and a JWT decode failure server-side
Root cause: the two AUTH_GOOGLE_*_CALLBACK_URL values are swapped. Twenty sends users to Google with the wrong redirect_uri, Google returns the auth code to the wrong endpoint, and the handler tries to exchange a code meant for a different flow
The swap can live in the database, NOT just .env
Because of TwentyConfigService Precedence, DB values in core."keyValuePair" silently override environment variables. Editing .env and restarting will have no effect if the DB rows still hold the swapped values. Always fix the DB directly.
URL-decode the redirect_uri parameter in the returned Google URL. It MUST point to /auth/google/redirect. If it points to /auth/google-apis/get-access-token, the values are swapped
Step 2: Check the Database
SELECT key, value FROM core."keyValuePair"WHERE key IN ('AUTH_GOOGLE_CALLBACK_URL', 'AUTH_GOOGLE_APIS_CALLBACK_URL');
Step 3: Fix the Swap in the Database
BEGIN;UPDATE core."keyValuePair" SET value = '"https://your-twenty-domain/auth/google/redirect"' WHERE key = 'AUTH_GOOGLE_CALLBACK_URL';UPDATE core."keyValuePair" SET value = '"https://your-twenty-domain/auth/google-apis/get-access-token"' WHERE key = 'AUTH_GOOGLE_APIS_CALLBACK_URL';COMMIT;
Note the double quoting: the value column stores JSON, so string values need '"..."'
Step 4: Restart the Server
docker compose restart server
Twenty reads config into memory on startup, so a restart is required for the fix to take effect. The worker container does NOT need to restart for the login flow
Deployment Behind Traefik
Twenty’s server exposes port 3000 internally and is routed via Traefik using Docker labels
If the Twenty instance does its own OAuth 2.0 login, do not apply Traefik’s forward-auth middleware to its router, as the two auth flows will conflict