Pular para o conteúdo
Kordyx Docs

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.

RequirementValue
HostAny machine that runs Docker with Linux containers. A Linux server, or a Mac or Windows PC with Docker Desktop
RuntimeDocker Engine 24 or newer, or Podman 5 or newer, with the Compose plugin v2.20.2 or newer
ProcessorIntel, AMD and Apple Silicon all work. Every image ships for amd64 and arm64, and Docker pulls the matching one
CPU2 vCPU minimum, 4 vCPU for a small active community. No service sets a CPU limit, so every container sees all of them
RAM8 GB minimum with the shipped defaults, 16 GB for a small active community. 4 GB needs the low-memory overrides
Disk20 GB minimum, more for uploads
Inbound ports80/tcp, 443/tcp and 443/udp for the web tier, plus 7881/tcp and 7882/udp for voice media
DNSAn A record for the hostname pointing at this machine, optionally an AAAA record
OutboundWorking DNS resolution from inside the containers
Host toolscurl 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.

Check what you have:

Janela do terminal
docker --version
docker compose version

If 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:

Janela do terminal
sudo apt-get install -y curl openssl

Swap 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.

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.

Open these ports on the host:

PortProtocolUsed by
22tcpSSH, on a host you reach remotely
80tcpACME HTTP validation and the redirect to HTTPS
443tcpThe web app, HTTP API, Gateway and admin dashboard
443udpHTTP/3 for the same traffic
7881tcpLiveKit media, published by the LiveKit container
7882udpLiveKit 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:

Janela do terminal
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=80/tcp --add-port=443/tcp --add-port=443/udp \
--add-port=7881/tcp --add-port=7882/udp
sudo firewall-cmd --reload

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:

Janela do terminal
curl -fsSLO https://fluxer.dev/install.sh
curl -fsSLO https://fluxer.dev/install.sh.sha256
sha256sum -c install.sh.sha256

On macOS, the same downloads and:

Janela do terminal
shasum -a 256 -c install.sh.sha256

On Windows:

Janela do terminal
Invoke-WebRequest https://fluxer.dev/install.ps1 -OutFile install.ps1
Invoke-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.

Janela do terminal
sh install.sh --domain chat.example.com

On Windows:

Janela do terminal
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Domain chat.example.com

The 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.

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=8443
FLUXER_HTTPS_PORT=8443
Janela do terminal
docker compose up -d

For 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.

FlagMeaning
--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-interactiveNever prompt. A missing required value is an error
--dry-runPrint the plan. Write nothing and start nothing
--no-startWrite everything and skip docker compose up -d
--updateUpgrade: record, back up, refresh, pull, recreate, verify
--rollbackRestore the images and stack files that the newest --update run recorded
--allow-rootPermit 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.

Watch the stack settle:

Janela do terminal
cd ~/fluxer
docker compose ps
docker compose logs -f api

Every 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:

Janela do terminal
base=https://chat.example.com
for 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"
done

On Windows:

Janela do terminal
$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.

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_TOKEN in .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.
Janela do terminal
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.

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.

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.

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.

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:

Janela do terminal
cd ~/fluxer
mkdir -p backups
docker 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:

Janela do terminal
mkdir -Force backups
docker 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.dump
docker compose exec -T postgres rm /tmp/fluxer.dump

The 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:

Janela do terminal
docker compose stop
docker 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 -d

In 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.

Take the instance down and delete its data:

Janela do terminal
docker compose down -v

The default tag v1 tracks the latest compatible release. The same script upgrades the instance:

Janela do terminal
cd ~/fluxer
sh install.sh --update

On Windows it is .\install.ps1 -Update.

Upgrading covers what the backup holds, rolling back, pinning a release and reclaiming disk.

File issues and follow development on GitHub.