Self-Hosting Guide

Deploy your own Fluxer server with Docker, configure it with JSON, and put it behind Caddy as a reverse proxy.

Contents

  1. Prerequisites
  2. Ports & Network
  3. Directory Structure
  4. Configuration (config.json)
  5. Docker Compose Setup
  6. Caddy Reverse Proxy
  7. Starting the Server
  8. Updating
  9. Troubleshooting

1. Prerequisites

Before starting, make sure you have the following installed on your server:

Note: Caddy automatically provisions and renews TLS certificates via Let's Encrypt. No manual certificate setup is needed.

Install Docker

# Install Docker (Ubuntu/Debian)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Verify installation
docker --version
docker compose version

Install Caddy

# Install Caddy (Ubuntu/Debian)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

2. Ports & Network

Understanding which ports are used and how traffic flows is important before deploying.

Required Ports

Port Protocol Service Exposure
80 TCP Caddy (HTTP) Public — redirects to HTTPS
443 TCP Caddy (HTTPS) Public — serves all external traffic
3000 TCP Fluxer API server Localhost only (127.0.0.1)
5432 TCP PostgreSQL (optional) Internal Docker network only

Traffic Flow

All external traffic goes through Caddy. The Fluxer server is never exposed directly to the internet.

User (browser/client)
  │
  │  HTTPS (:443)
  ▼
Caddy (reverse proxy)
  │
  │  ── HTTP redirect (:80 → :443)
  │
  │  HTTP (:3000, localhost only)
  ▼
Fluxer Server (Docker)
  │
  │  Internal Docker network
  ▼
PostgreSQL / SQLite

Redirects

Caddy handles the following redirects automatically:

Optional www redirect in your Caddyfile:

www.fluxer.example.com {
    redir https://fluxer.example.com{uri} permanent
}

Firewall Setup

If you use ufw, open only the ports Caddy needs:

# Allow HTTP (for ACME challenges and redirect)
sudo ufw allow 80/tcp

# Allow HTTPS
sudo ufw allow 443/tcp

# Allow SSH (don't lock yourself out)
sudo ufw allow 22/tcp

# Enable firewall
sudo ufw enable

# Verify
sudo ufw status

Do not open port 3000 in your firewall. The Fluxer server binds to 127.0.0.1:3000 and should only be accessible through the Caddy reverse proxy. Exposing it directly bypasses TLS and security headers.

Port 5432 (PostgreSQL) does not need a firewall rule. It runs inside the Docker network and is not exposed to the host at all.

3. Directory Structure

Create a directory for your Fluxer deployment:

mkdir -p /opt/fluxer/{config,data}
cd /opt/fluxer

Your final directory structure will look like this:

/opt/fluxer/
├── docker-compose.yml
├── config/
│   └── config.json
└── data/
    ├── db/            # Database files
    ├── uploads/       # User uploads
    └── logs/          # Server logs

4. Configuration (config.json)

The main configuration file controls how your Fluxer server behaves. Create it at /opt/fluxer/config/config.json.

Minimal Configuration

A basic configuration to get started:

config/config.json
{
  "server": {
    "name": "My Fluxer Server",
    "host": "0.0.0.0",
    "port": 3000,
    "publicUrl": "https://fluxer.example.com"
  },
  "database": {
    "type": "sqlite",
    "path": "/data/db/fluxer.db"
  },
  "storage": {
    "uploadsPath": "/data/uploads",
    "maxFileSize": "50MB"
  },
  "auth": {
    "registrationEnabled": true,
    "requireEmailVerification": false
  }
}

Full Configuration Reference

A more complete configuration with all common options:

config/config.json
{
  "server": {
    "name": "My Fluxer Server",
    "host": "0.0.0.0",
    "port": 3000,
    "publicUrl": "https://fluxer.example.com",
    "trustedProxies": ["127.0.0.1", "172.16.0.0/12"]
  },

  "database": {
    "type": "sqlite",
    "path": "/data/db/fluxer.db"
  },

  "storage": {
    "uploadsPath": "/data/uploads",
    "maxFileSize": "50MB",
    "allowedFileTypes": ["image/*", "video/*", "audio/*", "application/pdf"]
  },

  "auth": {
    "registrationEnabled": true,
    "requireEmailVerification": false,
    "sessionTimeout": "30d",
    "maxSessionsPerUser": 10
  },

  "email": {
    "enabled": false,
    "smtp": {
      "host": "smtp.example.com",
      "port": 587,
      "secure": true,
      "user": "noreply@example.com",
      "password": "your-smtp-password"
    },
    "from": "Fluxer <noreply@example.com>"
  },

  "rateLimit": {
    "enabled": true,
    "windowMs": 60000,
    "maxRequests": 100
  },

  "voice": {
    "enabled": true,
    "maxParticipants": 25
  },

  "logging": {
    "level": "info",
    "path": "/data/logs"
  }
}

Configuration Options

server.publicUrl — Must match the domain you configure in Caddy. This is used for generating invite links, email links, and API responses.

Security: If you enable email verification, make sure to configure the email.smtp section with valid SMTP credentials. Without it, users won't be able to verify their accounts.

5. Docker Compose Setup

Create a docker-compose.yml in /opt/fluxer/:

docker-compose.yml
version: "3.8"

services:
  fluxer:
    image: ghcr.io/fluxer/fluxer-server:latest
    container_name: fluxer_server
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - ./config:/usr/src/app/config:ro
      - ./data:/data
    environment:
      - NODE_ENV=production
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Why 127.0.0.1:3000? — Binding to 127.0.0.1 ensures the port is only accessible locally. Caddy will handle all external traffic and TLS termination.

Using PostgreSQL instead of SQLite

For larger deployments, you may want to use PostgreSQL. Add a database service to your compose file:

docker-compose.yml (with PostgreSQL)
version: "3.8"

services:
  fluxer:
    image: ghcr.io/fluxer/fluxer-server:latest
    container_name: fluxer_server
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - ./config:/usr/src/app/config:ro
      - ./data:/data
    environment:
      - NODE_ENV=production
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    container_name: fluxer_db
    restart: unless-stopped
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: fluxer
      POSTGRES_USER: fluxer
      POSTGRES_PASSWORD: change-this-password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U fluxer"]
      interval: 10s
      timeout: 5s
      retries: 5

Update your config.json database section to match:

{
  "database": {
    "type": "postgres",
    "host": "postgres",
    "port": 5432,
    "name": "fluxer",
    "user": "fluxer",
    "password": "change-this-password"
  }
}

Important: Change the default database password before deploying. Use a strong, randomly generated password.

6. Caddy Reverse Proxy

Caddy handles HTTPS and proxies requests to the Fluxer container. Edit the Caddyfile at /etc/caddy/Caddyfile.

Basic Setup

/etc/caddy/Caddyfile
fluxer.example.com {
    reverse_proxy 127.0.0.1:3000

    encode gzip zstd

    header {
        X-Content-Type-Options    nosniff
        X-Frame-Options           DENY
        Referrer-Policy           strict-origin-when-cross-origin
        -Server
    }

    log {
        output file /var/log/caddy/fluxer-access.log
        format json
    }
}

With WebSocket Support

If your Fluxer server uses WebSockets for real-time features, the basic config above already works — Caddy handles WebSocket upgrades automatically. For explicit configuration:

/etc/caddy/Caddyfile
fluxer.example.com {
    reverse_proxy 127.0.0.1:3000 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}

        # Timeouts for long-lived connections
        transport http {
            read_timeout  300s
            write_timeout 300s
        }
    }

    encode gzip zstd

    header {
        X-Content-Type-Options    nosniff
        X-Frame-Options           DENY
        Referrer-Policy           strict-origin-when-cross-origin
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        -Server
    }

    log {
        output file /var/log/caddy/fluxer-access.log {
            roll_size 100mb
            roll_keep 5
        }
        format json
    }
}

Multiple Services

If you're also hosting a web client on a different subdomain:

/etc/caddy/Caddyfile
# API / Server
fluxer.example.com {
    reverse_proxy 127.0.0.1:3000
    encode gzip zstd
}

# Web Client
app.example.com {
    root * /var/www/fluxer-web
    file_server
    try_files {path} /index.html
    encode gzip zstd
}

After editing the Caddyfile, reload Caddy:

sudo systemctl reload caddy

7. Starting the Server

1

Start the containers

cd /opt/fluxer
docker compose up -d
2

Check the logs

# Follow logs in real time
docker compose logs -f fluxer

# Check container status
docker compose ps
3

Verify Caddy

# Check Caddy status
sudo systemctl status caddy

# Test the endpoint
curl -I https://fluxer.example.com/api/health

Done! Your Fluxer server should now be accessible at https://fluxer.example.com. Caddy handles TLS certificates automatically.

8. Updating

To update your Fluxer server to the latest version:

cd /opt/fluxer

# Pull the latest image
docker compose pull

# Restart with the new image
docker compose up -d

# Verify it's running
docker compose ps
docker compose logs -f fluxer

Backups: Always back up your data/ directory before updating, especially the database. For SQLite: cp data/db/fluxer.db data/db/fluxer.db.bak. For PostgreSQL: docker exec fluxer_db pg_dump -U fluxer fluxer > backup.sql

9. Troubleshooting

Container won't start

# Check logs for errors
docker compose logs fluxer

# Verify config is valid JSON
python3 -m json.tool config/config.json

# Check file permissions
ls -la config/ data/

502 Bad Gateway from Caddy

TLS certificate issues

Database permission errors

# Fix ownership for SQLite
sudo chown -R 1000:1000 /opt/fluxer/data/db

# Fix ownership for uploads
sudo chown -R 1000:1000 /opt/fluxer/data/uploads

Check resource usage

docker stats fluxer_server

Need help? Join the community or open an issue on GitHub.

Join Fluxer World Back to Home