FATAL: role "app" does not exist means the user name you connected with is not a role on that server. PostgreSQL users are roles, and the official Docker image creates exactly one of them; any other name has to be created with CREATE ROLE ... LOGIN before it can connect.

Connect once as the superuser the image made (postgres unless you set POSTGRES_USER), run CREATE ROLE app LOGIN PASSWORD '...', then connect as app and name a database that exists.

The error

docker exec fix4-pg18 psql -U app
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  role "app" does not exist

psql does not print the SQLSTATE for a failed connection. With log_error_verbosity = verbose the server log shows it:

FATAL:  28000: role "app" does not exist
LOCATION:  InitializeSessionUserId, miscinit.c:802

Why it happens

PostgreSQL has no separate list of users. A user is a role with the LOGIN attribute, and the server checks the name you pass with -U (or Username= in a connection string) against that list before it looks at any password. Code 28000 is "invalid authorization specification": the server stopped at the name, so changing the password will not help.

The postgres Docker image runs initdb with one superuser, named by POSTGRES_USER and defaulting to postgres. A connection string copied from another project, or an appsettings.json that says Username=app, points at a role this fresh server has never heard of. The variable is also read only when the data directory is empty, so setting it on a container that already has a volume does not create the role either.

The fix

docker exec fix4-pg18 psql -U postgres -c "CREATE ROLE app LOGIN PASSWORD 'change-me-123'"
CREATE ROLE

docker exec -e PGPASSWORD=change-me-123 fix4-pg18 psql -h localhost -U app -d postgres -c "SELECT current_user, current_database()"
 current_user | current_database
--------------+------------------
 app          | postgres
(1 row)

fix4-pg18 and the password are the test's own; use your container name and a real secret. Note the -d postgres: straight after the role existed, psql -U app on its own failed again with FATAL: database "app" does not exist, because psql uses the user name as the database name when you give none. Name an existing database, or create one and make the new role its owner with CREATE DATABASE appdb OWNER app. The alternative is to recreate the container with POSTGRES_USER=app on an empty volume, which makes app the superuser; fine for a throwaway dev box, too much power for an application account.

How it was reproduced

A fresh postgres:18 container (PostgreSQL 18.4, Debian build) on Docker Desktop 4.91.0 under Windows 11, started with only POSTGRES_PASSWORD set and published on port 15460. Running psql -U app inside it produced the error above; the server log line came from the same attempt after ALTER SYSTEM SET log_error_verbosity = verbose and a reload. The CREATE ROLE and the TCP login were run straight after, with the output shown.

Frequently asked

How do I create a user in PostgreSQL?
Connect as a superuser and run CREATE ROLE name LOGIN PASSWORD 'secret'. CREATE USER does the same thing with LOGIN implied. A role without LOGIN cannot connect at all.
Why does PostgreSQL in Docker only have the postgres user?
The official image runs initdb with a single superuser named by POSTGRES_USER, which defaults to postgres. Any other role has to be created afterwards, or by a script in /docker-entrypoint-initdb.d on first start.
Does setting POSTGRES_USER on an existing container create the role?
No. The image reads POSTGRES_USER, POSTGRES_PASSWORD and POSTGRES_DB only when the data directory is empty. On a container with existing data, create the role with CREATE ROLE instead.

More decoded errors in the Fixes category. If the role exists and the server says the password is wrong instead, see password authentication failed for user "postgres".