Troubleshooting
The errors you can meet while installing Kinora, what causes each one, and how to fix it.
For the Full Stack package
Docker is not running
failed to connect to the docker API at unix:///…/docker.sock; check if the path is correct and if the daemon is runningOlder Docker versions say "Cannot connect to the Docker daemon" instead. Either way, the Docker engine is not started.
Open Docker Desktop
Start Docker Desktop and wait until it reports that the engine is running. On Linux, start the service:
sudo systemctl start docker.Check that Docker answers
Terminaldocker infoExpected result: It prints a Server section instead of an error.
Run your start command again
docker compose up --buildfor the full stack, or yourdocker buildanddocker runfor a single app.
A port is already in use
ports are not available: exposing port TCP 0.0.0.0:3030 … bind: address already in use
Error: listen EADDRINUSE: address already in use :::3030The first line comes from Docker, the second from yarn dev. Another program already listens on 3030 or 8000: often an earlier run of Kinora, or another project's dev server.
Find what holds the port
On macOS or Linux:
Terminallsof -i :3030On Windows, in PowerShell:
Terminalnetstat -ano | findstr :3030Stop it
Close that program, or stop the earlier run: Ctrl+C in its terminal,
docker compose downin its folder, ordocker stopfor a container you started withdocker run. Then start Kinora again.Or run Kinora on other ports
With the full stack in Docker, create a file named
.envin thekinora-fitness-full-stackfolder, next todocker-compose.yml, with the port you need.DASHBOARD_PORTmoves the dashboard andAPI_PORTthe API; the addresses the apps use follow by themselves.kinora-fitness-full-stack/.envDASHBOARD_PORT=3040Then run the same command again. After a failed start it picks up where it stopped:
Terminalinkinora-fitness-full-stackdocker compose up --buildExpected result: The dashboard answers on its new port, here localhost:3040Local.
Without Docker the ports are set in the .env files. To move the API there, change PORT in back-end/.env and both API addresses in dashboard/.env together. To move the dashboard, change PORT in dashboard/.env and CORS_ORIGIN in back-end/.env. With Docker, use API_PORT and DASHBOARD_PORT instead. For a single app started with docker run, change the number on the left of -p.
The Docker build fails
The first build downloads the base images and every dependency, then builds the images. It stops with "failed to solve" and the step that failed when something gets in the way.
- No connection or a timeout: the build needs internet access. Run the command again once you are online; finished steps are cached.
- No space left on device: free space in Docker Desktop, or check what Docker uses with
docker system df. - It fails at the same step every time: rebuild without the cache, then start.
kinora-fitness-full-stackdocker compose build --no-cache
docker compose upFor a single-app package, add --no-cache to your docker build command.
A first start that seems stuck is usually still seeding the demo data. The dashboard starts only once the API reports itself healthy.
Yarn says its version is 1.22
This project's package.json defines "packageManager": "yarn@4.8.1…". However the current global version of Yarn is 1.22…Each app pins Yarn 4 through Corepack. This message means Corepack is not enabled yet, so the old global Yarn answered instead.
corepack enableThen run yarn install again. If your Node.js has no Corepack, install it first with npm install -g corepack.
The API stops before starting
✖ The API cannot start: JWT_SECRET is not set. Set it in back-end/.env to the output of: …
✖ The API cannot start: JWT_SECRET is still the example value from .env.example, …
✖ The API cannot start: CORS_ORIGIN is not set. List the dashboard origin (comma separated if there are several), …The API checks its settings before it starts and prints one line per problem. The causes:
- There is no `.env` file, or `JWT_SECRET` is empty. Create the file in
back-end/withcp .env.example .env. - `JWT_SECRET` is still the example value with `NODE_ENV=production`. Anyone could sign a token with the public example, so a production start refuses it. Generate your own secret.
- `CORS_ORIGIN` is empty with `NODE_ENV=production`. Set it to the dashboard's address, comma separated if there are several.
Generate a secret with:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"With Docker you do not need one: a container without a JWT_SECRET of its own, or with the example one, generates a secret and keeps it in the data volume.
The health check answers 503
{"status":"unavailable"}
The database has no tables yet. Run `yarn seed` in back-end/, then restart the API./api/health answers 503 until the database answers and holds its tables. The second line is what the API's log says at start-up when the tables are missing.
- Without Docker, in development: run
yarn seedinback-end/(tables and demo data), oryarn db:sync(tables only), then restart the API. - With `NODE_ENV=production`: the API does not create tables by itself. Run
yarn db:sync:prod(empty tables) oryarn seed:prod(with the demo data) once, afteryarn build. - With Docker on SQLite: the container creates the tables by itself on first start. On MySQL it does not: its log says to run
node dist/database/sync-schema.js(tables) ornode dist/database/seeder.js(tables and demo data) once yourself. - "The database has no accounts, so nobody can sign in" means the tables exist but no account was ever added: run
yarn seedinback-end/.
MySQL refuses to start or to seed
The API creates its tables in a database that already exists; it does not create the database itself. Create an empty database first, give its name to DB_DATABASE in back-end/.env, then run yarn seed (or yarn db:sync for the tables with no demo data).
- Check the five
DB_*connection values and thatDB_TYPE=mysql. - With
NODE_ENV=productionthe running API never creates tables, so one of those commands runs before the first start:yarn db:sync:prodoryarn seed:prodafteryarn build.
Sign-in fails
- Check the account. Everyone signs in at the same form on port
3030: the Head Coach isheadcoach@example.comwithCoach@123, the membermember@example.comwithMember@123. - Check the API's log. "The database has no accounts, so nobody can sign in" means the demo data was never added. Without Docker, run
yarn seedinback-end/. With Docker, the volume was created withSEED_DEMO_DATA=false. - "The database has no tables yet" means the schema was never created: run
yarn seedinback-end/and restart the API. - You changed the Head Coach's password and no longer have it: start over with fresh demo data, below.
- "That email and password combination didn't work. Please try again." is the one answer for an unknown email and for a wrong password alike, so check both.
- "Too many attempts. Wait a minute and try again." means one address made more than 10 tries on the sign-in, registration or account-deletion route within a minute. Wait a minute and try again.
Sign-in fails
- Check the API's log. "The database has no accounts, so nobody can sign in" means the demo data was never added: run
yarn seed, or start the Docker container with-e SEED_DEMO_DATA=trueon a new volume. - "The database has no tables yet" means the schema was never created: run
yarn seedand restart the API. - Check the account. Everyone signs in at
POST /api/auth/login: the Head Coach isheadcoach@example.comwithCoach@123, the membermember@example.comwithMember@123. A failed sign-in answers401with the same message whether the email or the password is wrong. - A `429` answer means one address made more than 10 tries on the sign-in, registration or account-deletion route within a minute. The
Retry-Afterheader says how many seconds to wait.
Sign-in fails
On the sample data the password is not checked, but the form still asks for at least 6 characters. An email that belongs to a sample account signs in as that account, and any other email signs in as the Head Coach. Once you connect an API, sign in with an account that exists in it: the demo Head Coach is headcoach@example.com with Coach@123, and the demo member member@example.com with Member@123.
Every visitor gets "Too many attempts" behind a proxy
Sign-in, registration and account deletion allow 10 requests a minute per address and route, then answer 429. The API finds the visitor's address in X-Forwarded-For only when the request comes from a proxy on a private, loopback or platform address, as in Docker, on Railway or behind a reverse proxy on the same machine.
When your proxy reaches the API from a public address, every visitor looks like that one proxy and they share one allowance. Tell the API how many proxies stand in front of it:
TRUST_PROXY=1TRUST_PROXY=false never reads the header. Leave it unset when the API is reached directly or through a proxy on a private address. The counts live in the API's memory, so a restart clears them.
A "Sample data" notice appears
No API is connected (NEXT_PUBLIC_API_BASE_URL is empty), so the dashboard is running on built-in sample data: …The dashboard was started or built without NEXT_PUBLIC_API_BASE_URL, so it answers every request from sample data in the browser. What you change is then kept in the browser, not in the database.
Give the dashboard the API's address
Create
dashboard/.envfrom.env.exampleif it has none.NEXT_PUBLIC_API_BASE_URLmust be the API's address including/api, for examplehttp://localhost:8000/api, andNEXT_PUBLIC_WEBSOCKET_BASE_URLthe same server without it.Check that the API answers
Open localhost:8000/api/healthLocal. It should answer
{"status":"ok"}. If the address is set but the API does not answer, the pages cannot load their data: the sample data does not stand in for it.Restart or rebuild the dashboard
The address is compiled in. Restart
yarn devafter changing it, runyarn buildagain beforeyarn start, or with Docker rundocker compose up --buildagain.
A change to the dashboard's .env does nothing
Every NEXT_PUBLIC_* value is compiled into the JavaScript the browser loads when the app is built. Changing the file changes nothing until the app is built again.
| How you run it | After changing a value |
|---|---|
yarn dev | Stop it and run yarn dev again. |
yarn build and yarn start | Run yarn build again, then yarn start. |
| Docker Compose | Run docker compose up --build. Set API_URL, DASHBOARD_URL or MEDIA_HOSTNAME in the .env next to docker-compose.yml, not in the dashboard's folder. |
docker build for the dashboard | Build the image again with the value as a --build-arg. |
A feature says it is not set up
Uploads and the AI assistant switch on only when their keys are in back-end/.env. A key still holding its exact .env.example value counts as not set, so a copied .env.example starts cleanly and each of those features reports itself off instead of failing on its first call.
- Paste your real key over the example value, not next to it.
- Restart the API after changing
back-end/.env. With Docker Compose the API reads that file too, sodocker compose upagain is enough; a single API container needs--env-file .envon itsdocker run.
Uploads are refused
File storage is not configured on this server. Set the R2 variables in .env to enable uploads.Profile pictures, progress photos and message attachments go to a Cloudflare R2 or other S3-compatible bucket, and an upload answers 400 with this message until the five R2_* variables are set in back-end/.env. The demo data needs no bucket: its pictures are /assets/images/… paths the dashboard serves from its own public/ folder.
Requests are blocked on your own domains
Access to XMLHttpRequest at 'https://api.your-domain.com/api/…' from origin 'https://app.your-domain.com' has been blocked by CORS policyThe API answers browsers only from the addresses in CORS_ORIGIN, and its live sockets only from FRONTEND_URL when it is set (otherwise from the same list). Both default to the local dashboard on port 3030, so on your own domain they must name it.
CORS_ORIGIN=https://app.your-domain.com
FRONTEND_URL=https://app.your-domain.comWrite each address exactly as the browser shows it, with https:// and without a trailing slash, then restart the API. With Docker Compose, set DASHBOARD_URL and API_URL in the .env next to docker-compose.yml instead and run docker compose up --build: the compose file builds both values from them.
"Forgot password" sends nothing
The dashboard's forgot-password and reset-password pages are the screens only: no reset is sent, and no password changes through them. They call POST /api/auth/forgot-password and POST /api/auth/reset-password, which the template's API does not have, so against the API the form shows an error. On the sample data the form shows its confirmation, but nothing is sent there either. To give a member a new password, a staff account with members.update sets it on the member's page under Gym. A self-service reset needs both routes and a mail provider of your own.
The API container cannot find its entrypoint
exec /usr/local/bin/docker-entrypoint.sh: no such file or directoryThe script has Windows line endings, which an editor or Git on Windows can add. The shipped back-end/Dockerfile strips them while it builds, so this appears only with an image built from a changed Dockerfile. Keep its sed -i 's/\r$//' line, or save the script with LF line endings, then rebuild without the cache.
kinora-fitness-full-stackdocker compose build --no-cache api
docker compose upThe folder looks different
Run the commands inside the folder the ZIP extracts to. If the unzip command is missing, extract it with your file manager instead; some tools add an extra folder named after the ZIP, so move into the inner one.
| Package | Folder | Contains |
|---|---|---|
| Full Stack | kinora-fitness-full-stack | back-end, dashboard, docker-compose.yml |
| Dashboard | kinora-fitness-dashboard | Dockerfile, package.json, .env.example |
| Backend | kinora-fitness-backend | Dockerfile, package.json, .env.example |
Start over with fresh demo data
This deletes your data
Everything you created locally is removed, and the demo data is seeded again.
With Docker Compose, from the kinora-fitness-full-stack folder:
kinora-fitness-full-stackdocker compose down -v
docker compose upWith a single API container, remove the container first (docker ps -a lists it, docker rm -f with its id removes it), then delete the volume and run it again:
kinora-fitness-backenddocker volume rm kinora-data
docker run -p 8000:8000 -v kinora-data:/data -e SEED_DEMO_DATA=true kinora-apiWithout Docker, stop the API, then in its folder:
rm -f database.sqlite* && yarn seedIn PowerShell:
Remove-Item database.sqlite*; yarn seedRunning yarn seed again on a database you keep is not a reset: it adds only what is missing and never changes a row you edited. yarn db:reset drops every table, on SQLite and MySQL alike, before yarn seed.
Start over on the sample data
Without an API, your changes are kept in the browser. To bring the sample data back, run this in the browser's console on the dashboard's page:
localStorage.removeItem("mock_db_v1"); location.reload();