Bouée
Get started

Manual install

Install Bouée by hand, behind a reverse proxy of your own.

With a reverse proxy of your own, the same steps by hand.

1. Configure

Clone the Bouée repository onto the server, then create .env from the example and keep it private:

cp .env.example .env
chmod 600 .env

Fill in at least these. Every variable is described in .env.example, and in production Bouée refuses to use the example values of its own secrets, or any shorter than 32 characters.

VariableValue
APP_URLThe https address people will open, e.g. https://support.example.com. Sign-in, setup and the console answer there and nowhere else.
POSTGRES_PASSWORDopenssl rand -hex 32
BETTER_AUTH_SECRETopenssl rand -base64 32
PORTAL_LINK_SECRETopenssl rand -base64 32
SECRET_ENCRYPTION_KEYopenssl rand -base64 32. Optional but recommended: without it, changing BETTER_AUTH_SECRET makes the stored Resend connection unreadable.
S3_ACCESS_KEY_IDecho "GK$(openssl rand -hex 16)"
S3_SECRET_ACCESS_KEYopenssl rand -hex 32
GARAGE_RPC_SECRETopenssl rand -hex 32
GARAGE_ADMIN_TOKENopenssl rand -base64 32

Leave S3_BUCKET, S3_REGION="garage" and S3_FORCE_PATH_STYLE="true" as they are for the bundled Garage. DATABASE_URL and S3_ENDPOINT in .env are for local development only: Compose points the containers at its own database and storage. TRUSTED_PROXIES="1" is right for one reverse proxy on the same host. There is no BETTER_AUTH_URL to set; an older .env that still has one must match APP_URL, or the server will not start.

Keep a copy of .env somewhere safe and separate from the server: the secrets in it are needed to read a restored backup.

2. Start

docker compose --profile app up -d --build

The first run builds the image, applies the database migrations, and starts the web server on 127.0.0.1:3000 (WEB_BIND_ADDRESS changes the address). docker compose up -d without the profile starts only PostgreSQL and Garage, which is what local development uses.

Read the log once it is up:

docker compose --profile app logs web

It names the address Bouée answers at ([config] APP_URL is …), warns about anything that will not work as configured (an APP_URL that is unset, on localhost or plain http; an unset SECRET_ENCRYPTION_KEY), reports whether the attachment bucket answers ([storage] …), and prints the setup link ([setup] Setup link: …).

3. Point DNS at the server

Create an A record, and an AAAA record if the server has IPv6, for APP_URL's hostname. Brand hostnames need records of their own later; see Brand hostnames.

4. Put the reverse proxy in front

The proxy terminates TLS and forwards to 127.0.0.1:3000. It has to:

  • pass the original Host header through. Bouée decides from the Host what a request may reach: everything answers on APP_URL's host, and any other hostname is a brand's knowledge base and nothing more;
  • send X-Forwarded-Proto and X-Forwarded-For, appending the client's address: per-address limits on sign-in and setup read it, TRUSTED_PROXIES hops from the right;
  • accept request bodies of at least 64 MB, for attachments (up to 50 MB per message).

With Caddy, which obtains the certificate itself and preserves the Host by default:

support.example.com {
	request_body {
		max_size 64MB
	}
	reverse_proxy 127.0.0.1:3000
}

With nginx and a certificate you manage:

server {
    listen 443 ssl;
    server_name support.example.com;
    ssl_certificate     /etc/ssl/support.example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/support.example.com/privkey.pem;
    client_max_body_size 64m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Open https://support.example.com/api/health: it answers {"ok":true} once the app and its database are up.

On this page