Skip to the article
Aniq-UI

Food StudioTroubleshooting

Troubleshooting

The errors you can meet while installing the restaurant, what causes each one, and how to fix it.

For the Full Stack package

Docker is not running

What you see
failed to connect to the docker API at unix:///…/docker.sock; check if the path is correct and if the daemon is running

Older Docker versions say "Cannot connect to the Docker daemon" instead. Either way, the Docker engine is not started.

  1. 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.

  2. Check that Docker answers

    Terminal
    docker info

    Expected result: It prints a Server section instead of an error.

  3. Run your start command again

    docker compose up --build for the full stack, or your docker build and docker run for a single app.

A port is already in use

What you see
ports are not available: exposing port TCP 0.0.0.0:3031 … bind: address already in use
Bind for 0.0.0.0:3031 failed: port is already allocated
Error: listen EADDRINUSE: address already in use :::3031

The first two lines come from Docker, the last from yarn dev. Another program already listens on 3030, 3031 or 8000: often an earlier run of the restaurant, or another project's dev server.

  1. Find what holds the port

    On macOS or Linux, with the port from the message:

    Terminal
    lsof -i :3031

    On Windows, in PowerShell:

    Terminal
    netstat -ano | findstr :3031
  2. Stop it

    Close that program, or stop the earlier run: Ctrl+C in its terminal, docker compose down in its folder, or docker stop for a container you started with docker run. Then start the restaurant again.

  3. Or run the restaurant on other ports

    With the full stack in Docker, create a file named .env in the food-studio-full-stack folder, beside docker-compose.yml, with the port you need. SITE_PORT moves the website, ADMIN_PORT the dashboard and API_PORT the API; the addresses the apps use, CORS_ORIGIN included, follow by themselves.

    food-studio-full-stack/.env
    ADMIN_PORT=3041

    Then run the same command again. A failed start picks up where it stopped:

    Terminalin food-studio-full-stack
    docker compose up --build

    Expected result: The app answers on its new port, here localhost:3041Local.

For a single app started with docker run, change the number on the left of -p, for example -p 3041:3031, and add the new address to the API's CORS_ORIGIN. Without Docker the ports are fixed in the .env files: to move the API, change PORT in the API's .env and NEXT_PUBLIC_API_BASE_URL in both frontends together; to move a frontend, add its new address to CORS_ORIGIN.

Configuration

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.
Terminalin food-studio-full-stack
docker compose build --no-cache
docker compose up

For a single-app package, add --no-cache to your docker build command.

A first start that seems stuck is usually still seeding the demo restaurant. The website and the dashboard start only once the API reports itself healthy.

Yarn says its version is 1.22

What you see
This project's package.json defines "packageManager": "yarn@4…". 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.

Terminal
corepack enable

Then run yarn install again. If your Node.js has no Corepack, install it first with npm install -g corepack.

An install or a start fails on an older Node.js

Every app declares Node.js 24 or newer, and its Docker image runs on Node.js 24. Yarn does not stop an older version itself, so an older Node.js shows up later, as an install that fails to build a package or an app that stops at start.

Terminal
node -v

If it prints a version below 24, install Node.js 24 or newer, run corepack enable again, then delete the app's node_modules folder and run yarn install again. With Docker none of this applies: the images bring their own Node.js.

The website answers 500 in development

The first time yarn dev opens a page, Next.js downloads the site's Google fonts. Without internet access, or with fonts.googleapis.com blocked, every page answers 500 and the browser console names a font file, such as ibm_plex_sans_arabic.

Connect, then reload the page. yarn build and the Docker build download the fonts once, at build time, so a built site does not need them afterwards.

The API stops before starting

What you see
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, so anyone can sign a token for any account. …
The API cannot start: CORS_ORIGIN is not set. List the storefront and admin dashboard origins, comma separated, …

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 the API's folder with cp .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`. List the website's and the dashboard's addresses, comma separated.

Generate a secret with:

Terminal
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 menu is empty and nobody can sign in

What you see
→ No database at /data/database.sqlite. Creating the tables, no data (set SEED_DEMO_DATA=true for the demo).

The database was created with tables only. That happens when the API container starts on a new volume without SEED_DEMO_DATA=true, when the full stack runs with SEED_DEMO_DATA=false in the .env beside docker-compose.yml, or without Docker when yarn db:sync ran instead of yarn seed. There is no menu, no kitchen and no account to sign in with.

  • Without Docker: run yarn seed in the API's folder. It adds the demo restaurant and its accounts to the tables you have.
  • Full stack in Docker: remove SEED_DEMO_DATA=false from the .env, then docker compose down -v and docker compose up. The data volume is created again with the demo restaurant.
  • API container: remove the container, delete its volume, and run it again with -e SEED_DEMO_DATA=true, as in Start over below.

An existing database is never seeded again by the container, whatever SEED_DEMO_DATA says, so the variable only matters on a new volume.

Sign-in fails

  • Check the account and the app. Staff sign in to the dashboard at port 3031: owner@foodstudio.example with FoodDemo2026!. Customers sign in to the website at port 3030: sam@foodstudio.example with FoodDemo2026!. A customer account cannot open the dashboard, and a staff account cannot sign in on the website.
  • The dashboard says "We could not sign you in. Check your email and password, then try again." for a wrong password, an unknown email or a database with no accounts. After too many failed attempts it asks you to wait a few minutes instead. Check the points below in turn.
  • The database has no accounts when it was created with SEED_DEMO_DATA=false. See The menu is empty and nobody can sign in, above.
  • Ten failed sign-ins from one address within 15 minutes lock that sign-in form for that address. The API answers 429 until the oldest failure is 15 minutes old. Wait, or restart the API: the count is kept in its memory.
  • You changed the owner's password and no longer have it: start over with fresh demo data, below.

Sign-in fails

  • Check the account and the route. Staff sign in at POST /api/auth/login (the owner is owner@foodstudio.example with FoodDemo2026!); customers at POST /api/auth/customer/login (sam@foodstudio.example with FoodDemo2026!). A failed sign-in answers 401 with the same message whether the email or the password is wrong.
  • No account works at all: the database was created without the demo restaurant. Run yarn seed, or start the container with -e SEED_DEMO_DATA=true on a new volume.
  • A `429` answer means one address failed 10 sign-ins on that route within 15 minutes. It clears once the oldest failure is 15 minutes old, or when the API restarts. RATE_LIMIT_LOGIN changes the number.

Sign-in fails

Your app signs in against the template's API, so the account has to exist there. On a seeded API, staff sign in to the dashboard with owner@foodstudio.example and customers to the website with sam@foodstudio.example, both with FoodDemo2026!. If no account works, either the API was started without its demo data (run yarn seed in its folder, or start its container on a new volume with -e SEED_DEMO_DATA=true), or the app cannot reach the API: see the next problem.

Pages stay empty and the browser reports CORS

What you see
Access to fetch at 'http://localhost:8000/api/…' from origin 'http://localhost:3041' has been blocked by CORS policy

The browser's console shows this line when a frontend runs on an address the API does not accept. The API answers browsers only from the addresses in CORS_ORIGIN, which defaults to http://localhost:3030 and http://localhost:3031. A website or dashboard moved to another port, or served on your own domain, is refused until it is listed.

The API's .env
CORS_ORIGIN=http://localhost:3030,http://localhost:3041
FRONTEND_URL=http://localhost:3041
  • Write each address exactly as the browser shows it, with the scheme and the port and without a trailing slash, comma separated. Then restart the API.
  • FRONTEND_URL is the dashboard's address, which its live notifications connect from, and STOREFRONT_URL the website's, where a payment and a password-reset link send the customer. Move them together with the app.
  • For an API container, pass the same values with -e, such as -e CORS_ORIGIN=http://localhost:3030,http://localhost:3041.
  • With the full stack in Docker you do not edit these: SITE_PORT, ADMIN_PORT, SITE_URL and ADMIN_URL in the .env beside docker-compose.yml set them.
  • A frontend that cannot reach the API at all, because it is stopped or at another address, fails the same way without the CORS line. Check that localhost:8000/api/healthLocal answers, and that the frontend's NEXT_PUBLIC_API_BASE_URL names that API.

Dish pictures do not load

The seeded pictures are served by the API at /media/food-studio/…, and the seed writes each picture's full address from PUBLIC_MEDIA_URL, which defaults to http://localhost:8000/media. A picture breaks when that address does not reach the API from the browser.

  • The API runs on another port or domain. Set PUBLIC_MEDIA_URL to the API's public address followed by /media, for example -e PUBLIC_MEDIA_URL=http://localhost:8010/media on docker run -p 8010:8000. With the full stack in Docker, API_PORT and API_URL set it for you.
  • The API moved after the first start. Restart the API with PUBLIC_MEDIA_URL set to the new address (with Docker Compose, API_PORT or API_URL does it). On start it points every saved picture under /media/food-studio/ and /media/uploads/ at that address, and its log says how many it changed.
  • Uploads to a bucket do not load. R2_PUBLIC_URL must be the bucket's public address, and the bucket must allow public reads.
  • Pictures load, but slowly and at full size. The frontends resize only pictures from the https host in NEXT_PUBLIC_MEDIA_HOSTNAME (with Docker Compose, MEDIA_HOSTNAME), written as the host alone, such as pub-1234.r2.dev. Any other picture is shown as it is. Rebuild the frontend after changing it.

A change to a frontend'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 itAfter changing a value
yarn devStop it and run yarn dev again.
yarn build and yarn startRun yarn build again, then yarn start.
Docker ComposeRun docker compose up --build. Set the value in the .env beside docker-compose.yml, not in the app's folder.
docker build for one appBuild the image again with the value as a --build-arg.

New orders do not appear in the dashboard by themselves

The dashboard's bell and its live order updates use a WebSocket to the API. The dashboard connects to NEXT_PUBLIC_WEBSOCKET_BASE_URL, the API's address without /api, and the API accepts the connection only from FRONTEND_URL, the dashboard's own address.

  • Set both to match where the apps really run, then restart the API and rebuild the dashboard.
  • An unset FRONTEND_URL accepts only http://localhost:3031, so on any other address the live connection is refused.
  • A reload always shows the latest orders: only the live updates depend on the connection.

A feature says it is not connected

Card and PayPal payments, bucket uploads, password-reset mail, the AI assistant and the AI studio switch on only when their variables are set in the API's .env. In .env.example they are commented out, so a copied file starts cleanly and each of those features stays off.

  • Remove the # in front of the line and paste your real value over the example.
  • Restart the API after changing its .env. With Docker Compose the API reads back-end/.env too, so docker compose up again is enough; a single API container needs --env-file .env on its docker run.
  • Password-reset mail needs both RESEND_API_KEY and MAIL_FROM. Without them, "Forgot password" still answers as usual, and the log says "Mail is not configured: set RESEND_API_KEY and MAIL_FROM to send password reset messages."

A paid order stays unpaid

An order counts as paid only once the API has confirmed the payment with Stripe or PayPal, when the customer comes back or when the provider's webhook arrives. If an order stays unpaid after the customer paid:

  • The customer never came back to the API. The provider sends the browser to API_PUBLIC_URL, which defaults to http://localhost:8000. On your own domain, set it to the API's public address.
  • The webhook is not set up. Point it at your API's address followed by /api/payments/webhooks/stripe or /api/payments/webhooks/paypal, and set STRIPE_WEBHOOK_SECRET or PAYPAL_WEBHOOK_ID. An unsigned or altered call is refused with 401.
  • The customer left the payment page. An order nobody came back for is checked with the provider, after 35 minutes for Stripe and 3 hours for PayPal, and cancelled if it was not paid, which gives its stock and points back.

Checkout says the kitchen is closed or the address is outside the area

What you see
This kitchen is closed. Choose another kitchen.
Delivery is unavailable here. Try pickup.
  • Closed. A kitchen takes orders only during its hours, in its own timezone. With the service clock on Real, outside those hours checkout refuses delivery and pickup alike. Change the hours in Settings, Restaurant, or try another kitchen.
  • Outside the area. Delivery goes only to the postcodes a kitchen lists. The demo kitchens deliver to postcodes such as 10001 and 10002. Add your own postcodes to each kitchen in Settings, Restaurant.
  • Below the minimum. A delivery order needs at least the delivery minimum in food, $10.00 in the demo settings.

Every visitor gets "Too many attempts" behind a proxy

The public forms are limited per visitor address: orders, payment sessions, tracking, registration, the contact form, password resets and failed sign-ins. Behind a reverse proxy, every visitor can look like the proxy's one address and share one allowance. Tell the API how many proxies stand in front of it:

The API's .env
TRUST_PROXY=1

The RATE_LIMIT_* variables in .env.example change each limit. The counts live in the API's memory, so a restart clears them.

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 the API's .env, then run yarn seed (or yarn db:sync for the tables with no demo data).

  • Check the five DB_* connection values and that DB_TYPE=mysql.
  • With NODE_ENV=production the running API never creates tables, so one of those commands has to run before the first start (yarn seed:prod or yarn db:sync:prod after yarn build).
  • The Docker image creates the database by itself only on SQLite. On MySQL, run the seed or the schema command once yourself.

The API container cannot find its entrypoint

What you see
exec /usr/local/bin/docker-entrypoint.sh: no such file or directory

The script has Windows line endings, which an editor or Git on Windows can add. The shipped API 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.

The 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.

PackageFolderContains
Full Stackfood-studio-full-stackadmin-dashboard, back-end, storefront, docker-compose.yml
Ordering Websitefood-studio-websiteDockerfile, package.json, .env.example
Staff Dashboardfood-studio-staff-dashboardDockerfile, package.json, .env.example
Backend APIfood-studio-backendDockerfile, package.json, .env.example

Start over with fresh demo data

This deletes your data

Everything you created locally is removed, and the demo restaurant is seeded again.

With the full stack in Docker, from the food-studio-full-stack folder:

Terminalin food-studio-full-stack
docker compose down -v
docker compose up

With 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:

Terminal
docker volume rm foodstudio-data
docker run -p 8000:8000 -v foodstudio-data:/data -e SEED_DEMO_DATA=true foodstudio-api

Without Docker, stop the API, then in its folder:

Terminal
rm -f database.sqlite* && yarn seed

In PowerShell:

Terminal
Remove-Item database.sqlite*; yarn seed

Running yarn seed again on a database you keep is not a reset: it adds only what is missing and never changes a value you edited. yarn db:reset drops every table, on SQLite and MySQL alike; run yarn seed after it.

Stuck on a step?

Find a fix before you start over.

Troubleshooting

Cookie Preferences

We use cookies to enhance your browsing experience, analyze site traffic, and personalize content. By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising.