Docker Compose
This page helps you run Goiabada in production from the Docker Compose files the setup wizard writes.
The Compose files run the auth server, the admin console and the database on one host, and publish the two servers on 127.0.0.1 alone. A proxy on the same host puts them on the internet: Cloudflare Tunnel, Cloudflare + Nginx or Reverse proxy. Each of those pages runs this one first.
Run the wizard’s files
Section titled “Run the wizard’s files”-
Run the setup wizard in an empty directory, and choose Production with reverse proxy. Give it the two public URLs, such as
https://auth.example.comandhttps://admin.example.com, and a database. Or answer with flags:Terminal window ./goiabada-setup-linux-amd64 --type=production --db=postgres \--auth-url=https://auth.example.com --admin-url=https://admin.example.comIt writes
docker-compose.yml, which you can commit, anddocker-compose.override.ymlbeside it, which holds every secret. Never commit the override. -
Back up the AES key from the override before anything else: see Back up the AES key.
-
Start Goiabada with the command the wizard printed. With both files in the current directory, under their own names, it’s:
Terminal window docker compose up -dDocker Compose reads the override without being asked. The first start seeds the database, which can take a minute.
-
Check both servers answer on the host:
Terminal window curl http://127.0.0.1:9090/health # healthycurl http://127.0.0.1:9091/health # healthy -
Put a proxy in front, with one of the three pages above, and then sign in.
What the files run
Section titled “What the files run”- The database runs beside Goiabada, in a container of its own for MySQL, PostgreSQL and SQL Server, with its data in a named volume. With SQLite, the auth server keeps the database file in the
sqlite-datavolume. Database covers using a database server of your own instead. - The auth server starts once the database answers, and the admin console once the auth server does. A first start or an upgrade has up to five minutes to seed or migrate the database before Docker Compose counts the auth server unhealthy.
- Both servers publish their ports on
127.0.0.1alone, 9090 for the auth server and 9091 for the admin console. Nothing outside the host reaches them, so your proxy is the only way in. - Both servers trust one proxy hop to tell them the client’s IP address: see Client IP and proxy trust.
- Each server gets up to 60 seconds to stop when you run
docker compose downorrestart, so requests in flight finish. - The wizard’s rate limiter answer, yes unless you said no, is written as
GOIABADA_AUTHSERVER_RATELIMITER_ENABLED. See Rate limits.
The hop to the auth server
Section titled “The hop to the auth server”The admin console calls the auth server at GOIABADA_AUTHSERVER_INTERNALBASEURL, which the wizard sets to http://goiabada-authserver:9090: straight to the auth server’s container, over the Compose network, without passing your proxy.
That hop is plain HTTP, and it carries the admin console’s client secret, its refresh token and administrators’ tokens. A Compose network never leaves the host, so the hop is as safe as the host is. The generated file says so in a comment above the variable.
When that isn’t enough, encrypt the hop with the auth server’s own HTTPS listener:
-
Make a certificate for the name the admin console calls,
goiabada-authserver, from a certificate authority of your own, and mount it into the auth server’s container, readable by the user the images run as. -
Turn on the auth server’s HTTPS listener, in its
environment:- "GOIABADA_AUTHSERVER_LISTEN_HOST_HTTPS=0.0.0.0"- "GOIABADA_AUTHSERVER_LISTEN_PORT_HTTPS=9443"- "GOIABADA_AUTHSERVER_CERTFILE=/certs/goiabada-authserver.pem"- "GOIABADA_AUTHSERVER_KEYFILE=/certs/goiabada-authserver-key.pem" -
Mount your certificate authority’s certificate into the admin console’s container, then point the admin console at the listener and have it trust that authority, in its
environment:- "GOIABADA_AUTHSERVER_INTERNALBASEURL=https://goiabada-authserver:9443"- "SSL_CERT_FILE=/certs/ca.pem"SSL_CERT_FILE, orSSL_CERT_DIRfor a directory of certificates, adds your authority to the ones the image already trusts. To trust yours alone, set both,SSL_CERT_DIRto a directory holding only your certificates. -
Restart both servers with
docker compose up -d.
If the admin console then can’t reach the auth server, see Unable to load the configuration from the auth server.
The user the images run as
Section titled “The user the images run as”Both images run as uid 10001 and gid 10001, a user and group named goiabada, and never as root. The image names them by number, so a platform that refuses a root container, such as Kubernetes with runAsNonRoot, can verify the user without reading the image’s /etc/passwd. The binary and the /app directory holding it belong to root, so the process can’t replace what its next start runs.
A file you mount into a container must be readable by uid 10001 or gid 10001: the certificate and key named by GOIABADA_AUTHSERVER_CERTFILE and GOIABADA_AUTHSERVER_KEYFILE (and the admin console’s pair), and the templates and static files of a customization. A key file left at mode 0600 and owned by root can’t be read; give it to the user, or to the group with mode 0640:
sudo chown 10001:10001 key.pem# orsudo chgrp 10001 key.pem && sudo chmod 0640 key.pemThe auth server writes to two directories, and its image creates both owned by 10001:
/data, where the generated Compose file mounts the SQLite volume. Docker copies a mount point’s ownership into an empty named volume, so a new volume mounted here is writable from the first start. A volume mounted at a path the image lacks gets a directory Docker creates owned by root, where the server can write nothing./bootstrap, where the legacy bootstrap writesbootstrap.envwhenGOIABADA_AUTHSERVER_BOOTSTRAP_ENV_OUTFILEnames a file in it. Mount a named volume there, and read the file withdocker compose cp goiabada-authserver:/bootstrap/bootstrap.env .. A bind mount of a host directory that doesn’t exist yet is created owned by root, and the write is refused.
The generated file also drops every capability, keeps the root file system read-only and gives each server a /tmp in memory.