Deploying with Coolify
Running an installation on a Coolify server from the published image.
Coolify is a self-hosted platform that runs Docker resources on your own servers, with a built-in proxy that issues TLS certificates. This page deploys Alba Ticket on it from the published image, with PostgreSQL managed by Coolify and everything else in one Compose stack. It assumes a Coolify server that already has a working proxy (Traefik, the default) and a project to put the resources in.
The result is one application container, RustFS for attachments, Typesense for search, and a Coolify PostgreSQL database with scheduled backups. Email goes to your SMTP provider.
Before you start
- Two DNS names pointing at the Coolify server: one for the application (
tickets.example.combelow) and one for attachments (files.example.com). Browsers upload attachments straight to RustFS, so it needs its own public name; see Reverse proxy. - An SMTP account for login links and notifications. There is no local mail catcher in this stack.
- The two secrets, generated on your machine and kept somewhere safe; losing
CLOAK_KEYmakes stored credentials unreadable:
openssl rand -base64 64 | tr -d '\n' # SECRET_KEY_BASE
openssl rand -base64 32 # CLOAK_KEY
The published images at ghcr.io/garth/alba are public, so the server needs no registry login. Pick the version to run from Published images; latest is the newest release.
1. Create the database
In your project's environment choose New Resource and pick PostgreSQL under Databases. Keep the defaults (PostgreSQL 16 or newer) and start it. Then:
- On the database's page, copy the Internal URL. It looks like
postgres://postgres:<password>@<container>:5432/postgresand works asDATABASE_URLas it is. - Leave Make it publicly available off; the application reaches the database over the Docker network.
- Under Backups, add a schedule (daily is the usual choice) and an S3 destination. This is the database backup described in Backups; the rest of that page still applies to attachments and secrets.
2. Create the application stack
Choose New Resource again and pick Docker Compose Empty under Services. Paste the contents of compose.coolify.yaml and save.
Coolify reads the file and turns every ${VARIABLE:?} into a required field. Open the resource's Environment Variables page and fill them in:
| Variable | Value |
|---|---|
DATABASE_URL |
The Internal URL copied from the database. |
PHX_HOST |
The application's host name, tickets.example.com, without a scheme. |
SECRET_KEY_BASE, CLOAK_KEY |
The two secrets generated above. |
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_TLS |
Your mail provider; see Configuration. |
MAIL_FROM |
The sender address, on a domain the provider lets you send from. |
STORAGE_PUBLIC_BASE_URL |
https://files.example.com, the attachments host name. |
ALBA_VERSION |
Optional. The image tag to run, for example 0.7.8, this release; leave it unset for latest. |
Coolify generates SERVICE_PASSWORD_RUSTFS and SERVICE_PASSWORD_TYPESENSE itself and keeps them across redeploys; leave them alone. REGISTRATION_MODE defaults to invite only.
The pasted file is the whole of what the containers see. Coolify substitutes a variable from the Environment Variables page into the ${...} references in the file and nowhere else, so a variable the file does not name never reaches the application, and nothing tells you. To set one of the other variables from Configuration — PLAUSIBLE_DOMAIN, CSP and the others — add a line for it to the file's x-app-environment block first, as NAME: ${NAME:-}, and the page then shows a field for it.
Then, still on the stack:
- In the Domains field for the
appservice enterhttps://tickets.example.com. For therustfsservice enterhttps://files.example.com:9000; the port tells the proxy to route the public name to RustFS's API port. Both must match the values you put inPHX_HOSTandSTORAGE_PUBLIC_BASE_URL. - Turn on Connect To Predefined Network so the stack can reach the database resource by its internal name.
- Deploy. After changing that setting or an environment variable later, use Redeploy rather than Restart: both reach the containers only when Coolify recreates them.
The migrate service runs the database migrations and exits, and the app service starts once it has finished and RustFS and Typesense answer; the application creates the attachments bucket itself on first start. The proxy requests certificates for both names as soon as DNS resolves to the server. Watch the deployment log, then the app container's log, for the line saying the endpoint is listening.
3. First visit
Open https://tickets.example.com. An installation with no users shows the setup page: it creates the first administrator, with a password so that login works before email delivery is confirmed, and names the installation. Attachment storage is registered automatically on first start from the RustFS service, so uploads work at once; check under Administration, Storage that the location verifies.
Send yourself an invitation from Invitations to confirm mail delivery, and run Rebuild index on the Search page under Administration if search returns nothing (a fresh Typesense holds no data until the first index run).
Upgrading
- Take a database backup from the database's Backups page (or wait for the scheduled one).
- Set
ALBA_VERSIONto the new release tag on the Environment Variables page, or leave it onlatestand pull. - Press Redeploy. Coolify pulls the image,
migrateruns the new migrations once, andapprestarts only after they succeed. If migration fails, the old application container keeps running; the deployment log anddocker logsof the migrate container show why.
The notes in Upgrades about specific versions apply here too.
Importing from Jira
Upload the backup under Administration, Jira import. It goes straight from the browser to the storage bucket, under an imports/ key of its own, and the import reads it from there; nothing has to be copied onto the server. An upload is removed after a day.
Operating notes
- Health. The image carries a health check on
/health, and Coolify shows the container as healthy or unhealthy from it. The proxy stops routing to an unhealthy container. - Logs. The Logs tab on the stack shows every service; the application logs structured text on stdout. Look there first for
Postgrexconnection errors, Oban job failures and mail delivery errors. - Attachments live in the
rustfsdatavolume; include it in your backup plan as described in Backups. RustFS's console is off in this stack; to look inside the bucket, point an S3 client such asrcloneathttps://files.example.comwith the keyalbaand the value ofSERVICE_PASSWORD_RUSTFS. - Search data is derived and needs no backup; a new Typesense volume is refilled with Rebuild index.
- Resources. The application is comfortable with 1 GB of memory; RustFS and Typesense want a further 512 MB each to start with. See Requirements.
- Several servers. Coolify runs this stack on one server. Running more than one application node is described in Clustering and is not something Coolify's Compose resources do for you.
Troubleshooting
- The deployment sits at "migrate", fails with
service "migrate" didn't complete successfully, or the app never becomes healthy. Almost alwaysDATABASE_URL: confirm the stack has Connect To Predefined Network on and the URL is the database's Internal URL, not the public one. Coolify hides containers that have exited, so read the reason on the server withdocker logsand the migrate container's name from the deployment log.non-existing domain - :nxdomainthere means the stack is not on the database's network: turn the setting on and Redeploy, and check thatdocker network inspect coolifylists both the database and the stack's containers. error from registry: unauthorizedwhile pulling the image. The tag inALBA_VERSIONdoes not exist; the registry gives the same answer for a missing tag as for a private image. Pick one from Published images.- Uploads fail in the browser but the storage location verifies.
STORAGE_PUBLIC_BASE_URLdoes not match therustfsdomain, or that domain lacks the:9000port suffix in Coolify. The application talks to RustFS internally; only browsers use the public name, and RustFS answers them only from the address inPHX_HOST, which the file passes to it asRUSTFS_CORS_ALLOWED_ORIGINS. - Login links point at the wrong address.
PHX_HOSTmust equal the app domain without a scheme;PHX_SCHEMEis alreadyhttps. - Pages load but nothing updates live. The proxy must pass WebSocket upgrades; Coolify's Traefik does by default, so check that no custom proxy configuration strips them. The browser console shows the
/live/websocketconnection state. - Certificates are not issued. DNS for both names must resolve to the server and ports 80 and 443 must be open before the first deploy; the proxy's log under Servers, Proxy, Logs shows the ACME exchange.
See Troubleshooting for problems that are not specific to Coolify.