Get started
By the end you have a Fluxer instance on a hostname you own, running the web app, HTTP API, Gateway, admin dashboard, uploads, search and voice signalling in Docker containers on one machine. Allow about 30 minutes, most of it spent downloading images.
To run the same instance on a Kubernetes cluster, follow Kubernetes instead.
What you need
Seção intitulada “What you need”| Requirement | Value |
|---|---|
| Host | Any machine that runs Docker with Linux containers. A Linux server, or a Mac or Windows PC with Docker Desktop |
| Runtime | Docker Engine 24 or newer, or Podman 5 or newer, with the Compose plugin v2.20.2 or newer |
| Processor | Intel, AMD and Apple Silicon all work. Every image ships for amd64 and arm64, and Docker pulls the matching one |
| CPU | 2 vCPU minimum, 4 vCPU for a small active community. No service sets a CPU limit, so every container sees all of them |
| RAM | 8 GB minimum with the shipped defaults, 16 GB for a small active community. 4 GB needs the low-memory overrides |
| Disk | 20 GB minimum, more for uploads |
| Inbound ports | 80/tcp, 443/tcp and 443/udp for the web tier, plus 7881/tcp and 7882/udp for voice media |
| DNS | An A record for the hostname pointing at this machine, optionally an AAAA record |
| Outbound | Working DNS resolution from inside the containers |
| Host tools | curl and openssl on Linux. macOS ships both. The Windows installer needs neither |
Memory limits are ceilings. On a 4 GB host, lower the api and worker limits first. With Docker Desktop, check the RAM of its Linux virtual machine.
The edge and LiveKit are the only services that publish ports.
The steps below use the bundled Caddy, which obtains and renews a TLS certificate for the hostname. To put your own proxy in front, add the docker-compose.proxy.yml overlay. The stack then leaves 80 and 443 alone and publishes one plain HTTP port for your proxy to forward to. Behind your own reverse proxy has the overlay, the requirements, and worked configuration for nginx, Caddy, Traefik, HAProxy, Apache, a Cloudflare Tunnel and Nginx Proxy Manager.
Step 1: Install Docker
Seção intitulada “Step 1: Install Docker”Check what you have:
docker --versiondocker compose versionIf both answer, skip ahead. On a Mac or a Windows PC, install Docker Desktop and leave it on Linux containers, which gives you both.
Debian and Ubuntu need Docker’s apt repository from Install Docker Engine, because the distribution’s own docker.io package ships no Compose plugin. Follow the post-installation steps to add your user to the docker group, then log out and back in so the commands below run without sudo.
Linux also needs curl and openssl, which the steps below use:
sudo apt-get install -y curl opensslSwap apt-get for dnf, zypper, pacman or apk elsewhere. The package names are the same, and curl --version and openssl version both answer once the install finishes.
Step 2: Point DNS at the host
Seção intitulada “Step 2: Point DNS at the host”Choose the hostname the instance answers on. Step 4 writes it into .env as FLUXER_DOMAIN, and every example below uses chat.example.com in its place.
Create an A record for that hostname pointing at the machine’s public address, and an AAAA record when the machine has a public IPv6 address. A hosting provider shows that address in its console. On a home connection the address belongs to the router, and the router’s status page shows it.
Step 3: Open the ports
Seção intitulada “Step 3: Open the ports”Open these ports on the host:
| Port | Protocol | Used by |
|---|---|---|
| 22 | tcp | SSH, on a host you reach remotely |
| 80 | tcp | ACME HTTP validation and the redirect to HTTPS |
| 443 | tcp | The web app, HTTP API, Gateway and admin dashboard |
| 443 | udp | HTTP/3 for the same traffic |
| 7881 | tcp | LiveKit media, published by the LiveKit container |
| 7882 | udp | LiveKit media, published by the LiveKit container |
A machine behind a home router needs 80, 443 and both LiveKit ports forwarded to it. The certificate is issued from the public internet, and voice media arrives on the LiveKit ports directly. Behind your own reverse proxy, the proxy holds 80 and 443 and the stack binds a plain HTTP port on the loopback. 7881 and 7882 still have to reach the host.
For another port, open that one instead of 443 and read Serving on another port.
On a cloud VM, open them in the provider’s firewall or security group. On a Linux host running firewalld:
sudo firewall-cmd --permanent --add-service=sshsudo firewall-cmd --permanent --add-port=80/tcp --add-port=443/tcp --add-port=443/udp \ --add-port=7881/tcp --add-port=7882/udpsudo firewall-cmd --reloadStep 4: Bring up the instance
Seção intitulada “Step 4: Bring up the instance”The installer downloads the stack files, writes .env, generates every secret, and starts the instance. Download it, check the published SHA-256, read it, then run it.
On Linux:
curl -fsSLO https://fluxer.dev/install.shcurl -fsSLO https://fluxer.dev/install.sh.sha256sha256sum -c install.sh.sha256On macOS, the same downloads and:
shasum -a 256 -c install.sh.sha256On Windows:
Invoke-WebRequest https://fluxer.dev/install.ps1 -OutFile install.ps1Invoke-WebRequest https://fluxer.dev/install.ps1.sha256 -OutFile install.ps1.sha256(Get-FileHash .\install.ps1 -Algorithm SHA256).Hash -eq (Get-Content .\install.ps1.sha256).Split()[0]install.sh: OK on Linux and macOS, and True on Windows, mean the file arrived whole. The digest of the copy this site serves is 1dd1a31159253cb0f4708e05006479ecaacd7e0c6f14c02f1c65a22d6b202fc3 for install.sh and 2488f5e4b8c539cd2c9bc04855ceead0e07189705a9afcc3de0b71aa856f6c5d for install.ps1.
Read the script before you run it. It writes .env, and that file holds every secret the instance has.
Run it with the hostname from Step 2. The command below uses the bundled TLS. When something else terminates TLS, add --tls proxy after reading Behind your own reverse proxy.
sh install.sh --domain chat.example.comOn Windows:
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Domain chat.example.comThe run prints one line per phase and ends with the URL to open.
~/fluxer then holds docker-compose.yml, docker-compose.proxy.yml, tunnel.compose.yml, external-object-store.compose.yml, Caddyfile and .env.example, plus a .env readable only by you. Every value that ships as CHANGE_ME in .env.example is a fresh random value. The installer also writes FLUXER_SETUP_TOKEN, the setup token that Step 6 asks for. Every command from here on runs in that directory.
Serving on another port
Seção intitulada “Serving on another port”The installer writes an .env for https on 443 and has no flag for another port. Set both lines below in .env. FLUXER_PUBLIC_PORT is the port of the public address, and every endpoint the services advertise follows it. FLUXER_HTTPS_PORT is the host port the edge publishes HTTPS on.
FLUXER_PUBLIC_PORT=8443FLUXER_HTTPS_PORT=8443docker compose up -dFor plain HTTP on the new port, also set FLUXER_PUBLIC_SCHEME=http, and set FLUXER_HTTP_PORT in place of FLUXER_HTTPS_PORT. .env.example ships beside .env and writes out both complete recipes.
Host 80 stays published on the https recipe and still answers the ACME challenge. Let’s Encrypt only ever connects to the public 80 or 443, so the certificate is issued if a router in front forwards public 80 to this host. Serve your own certificate from the Caddyfile when it cannot.
Leave FLUXER_PUBLIC_ORIGIN commented out, or keep it consistent with the public scheme, domain and port. Conflicting addresses can cause sign-in failures and setup loops. See The public origin.
Installer flags
Seção intitulada “Installer flags”| Flag | Meaning |
|---|---|
--domain <host> | Hostname the instance answers on. Prompted when absent |
--email <address> | Contact address on outgoing web push, written as FLUXER_VAPID_EMAIL. Default admin@ and the hostname |
--dir <path> | Working directory. Default ~/fluxer, or the current directory when it holds an instance |
--ref <git ref> | Ref the stack files come from. Defaults to the image tag, and to main when that tag is v1 or latest |
--image-tag <tag> | Image tag the stack runs. Default v1 |
--tls <mode> | bundled or proxy. Default bundled |
--edge-bind <addr:port> | Plain HTTP bind under --tls proxy. Default 127.0.0.1:8080 |
--non-interactive | Never prompt. A missing required value is an error |
--dry-run | Print the plan. Write nothing and start nothing |
--no-start | Write everything and skip docker compose up -d |
--update | Upgrade: record, back up, refresh, pull, recreate, verify |
--rollback | Restore the images and stack files that the newest --update run recorded |
--allow-root | Permit running as root |
--engine <command> | Container engine to drive. Default docker, or podman when docker is absent |
Run --dry-run first to see what a set of flags does. The PowerShell script takes the same flags as -Domain, -Email and so on, and has no -AllowRoot. Upgrading has the flags that only --update and --rollback read, and the exit codes.
Step 5: Check that it works
Seção intitulada “Step 5: Check that it works”Watch the stack settle:
cd ~/fluxerdocker compose psdocker compose logs -f apiEvery service reads running or healthy except seaweedfs-init, which creates the upload buckets and then reads exited (0).
Then the public probes. Replace the origin with your own, port included when the instance answers on one:
base=https://chat.example.comfor p in /_health /api/_health /gateway/_health /media/_health /.well-known/fluxer /; do printf '%s %s\n' "$(curl -sS -o /dev/null -w '%{http_code}' "$base$p")" "$p"doneOn Windows:
$base = 'https://chat.example.com'foreach ($p in '/_health', '/api/_health', '/gateway/_health', '/media/_health', '/.well-known/fluxer', '/') { '{0} {1}' -f (Invoke-WebRequest "$base$p" -UseBasicParsing).StatusCode, $p}Every path returns 200. /.well-known/fluxer lists the endpoints clients use, with your own hostname in them.
Step 6: Create the first admin
Seção intitulada “Step 6: Create the first admin”Open the hostname in a browser. A fresh instance is unconfigured, so it serves the setup wizard.
After the welcome screen the wizard asks for the setup token. The token proves that you run the instance, and nobody can create an account on an unconfigured instance without it.
- The installer prints the token as its last step, and keeps it as
FLUXER_SETUP_TOKENin.env. - An instance brought up by hand without that variable has a token the API generated. The API writes it to its log once, on the first start.
docker compose logs api | grep -i 'setup token'When the token is lost, set FLUXER_SETUP_TOKEN in .env and run docker compose up -d.
The mobile app has no field for the token, so do the first-run setup in a browser. When the browser is already signed in to an account that is not an admin, the wizard says so and offers to sign out.
The wizard then asks how members sign in. Usernames is the default and needs no email setup at all. Email suits an instance that sends mail through SMTP. The choice is fixed once the owner account exists. Sign-in methods explains both.
Then create the owner account, and choose branding, registration mode, community policy, media expiry, integrations and premium settings.
The registration that presents the setup token receives the wildcard admin ACL, which is full access to the admin dashboard, in every registration mode. On a username instance that registration needs a password. On an email instance it needs an email address. The token stops working once the admin account exists.
On a username instance the wizard shows the owner’s recovery kit next. Store it somewhere safe. Without the kit, a forgotten owner password needs another admin to issue a reset link. Register a passkey or a second admin account before you open registration.
On an email instance
Seção intitulada “On an email instance”Create the owner account with an email address at a domain you control. Email delivery is optional, and Email sets it up at any time.
Once email is on, registration checks that the address domain can receive mail. A .lan, .internal or home.arpa address fails. Use a domain with public records, or leave email off.
.env.example ships FLUXER_EMAIL_ENABLED=false, which marks every address verified at creation and sends no mail at all. A forgotten owner password therefore has no email reset. Record it, and register a passkey or a second admin account before you open registration.
The admin dashboard
Seção intitulada “The admin dashboard”Sign in to the admin dashboard at https://chat.example.com/admin with the account holding the wildcard ACL. The Instance Config page has everything the wizard asked, plus registration mode, approvals and integration keys. Limit Config holds the instance limits published to clients. Voice Regions and Voice Servers come seeded, so voice needs no setup there.
Configuration lists every runtime setting and the environment variable each one overrides. Deployment availability lists the routes that exist only on the hosted deployment.
Connect the apps
Seção intitulada “Connect the apps”The web app is at your instance address. The desktop app asks for an instance first. Enter the instance address, such as chat.example.com. One desktop app can hold accounts on several instances and switch between them. The mobile apps take the same address.
The Flathub build of the desktop app may not connect to an instance yet. Its update is pending. To skip the wait, uninstall the Flathub copy and install our own Flatpak from Linux repositories. It uses the same application id, app.fluxer.Fluxer.
Backups
Seção intitulada “Backups”Recovery requires .env, a database dump and a backup of seaweedfs-data, which holds uploads, avatars, reports and harvests. See Volumes and buckets for everything that needs backing up.
The dump costs no downtime, so run it on a schedule while the stack serves. It runs inside the postgres container and takes the role and database from that container’s environment, which follows FLUXER_POSTGRES_USERNAME and FLUXER_POSTGRES_DATABASE. It is for the bundled database. A database outside the stack is backed up with its own tools:
cd ~/fluxermkdir -p backupsdocker compose exec -T postgres sh -c 'pg_dump -U $POSTGRES_USER -d $POSTGRES_DB --format=custom' \ > "backups/fluxer-$(date -u +%Y%m%dT%H%M%SZ).dump"PowerShell writes UTF-16 through >, which corrupts a binary dump. On Windows, write the dump inside the container and copy it out:
mkdir -Force backupsdocker compose exec -T postgres sh -c 'pg_dump -U $POSTGRES_USER -d $POSTGRES_DB --format=custom -f /tmp/fluxer.dump'docker compose cp postgres:/tmp/fluxer.dump backups/fluxer.dumpdocker compose exec -T postgres rm /tmp/fluxer.dumpThe uploads copy stops the stack, because a live copy can catch a file mid-write. Compose prefixes volume names with the project name, which the stack sets to fluxer:
docker compose stopdocker run --rm -v fluxer_seaweedfs-data:/data -v "$PWD/backups:/backup" alpine:3.22 \ tar czf /backup/seaweedfs-data.tgz -C /data .docker compose up -dIn PowerShell write ${PWD} in place of $PWD.
You are done when backups holds a dump and a tarball and every service reads running again.
sh install.sh --update takes both backups before upgrading. See What the backup covers for scheduled backups and Restore a backup for recovery.
Remove the instance
Seção intitulada “Remove the instance”Take the instance down and delete its data:
docker compose down -vUpgrading
Seção intitulada “Upgrading”The default tag v1 tracks the latest compatible release. The same script upgrades the instance:
cd ~/fluxersh install.sh --updateOn Windows it is .\install.ps1 -Update.
Upgrading covers what the backup holds, rolling back, pinning a release and reclaiming disk.
Getting help
Seção intitulada “Getting help”File issues and follow development on GitHub.
Kordyx · Documentação adaptada do Fluxer, sob AGPLv3.Código-fonte e licenças ·Abrir Kordyx ·Aplicativos