installation guide

Installing EdgeWarden

The complete walkthrough for bringing the platform up on a Debian 13 server (minimal install), from dependencies to a working dashboard.

  • Debian 13 (Trixie)
  • x86_64
  • ~40 minutes
  • root access
Debian 13 · x86_64dashboard on port 3099
Guide contents

Before you start

read once Technical names use the flowspec prefix. To stay compatible with existing installations, the directory (/opt/flowspec-analyzer), the services (flowspec-core, flowspec-frontend and flowspec-analyzer.target), the ClickHouse database (flowspec), the package (flowspec-analyzer-stable.tar.gz) and the API keys (fsk_) keep that prefix. The commands in this guide are correct for the current release.

Hardware requirements

Pick a size based on your network's flow volume. All three run the same package.

ResourceSmall: regional ISPMedium (recommended): mid-size ISP or DCEnterprise: Tier-1 or large DC
VolumeUp to 20k flows/s, networks up to ~5 GbpsUp to 50k flows/s, networks up to ~20 Gbps100k+ flows/s, networks of 40 Gbps or more
CPU8 cores (Xeon / EPYC)16 cores (Xeon Scalable / EPYC)32+ cores (EPYC / Xeon Platinum)
RAM32 GB DDR464 GB DDR4 ECC128+ GB DDR4/DDR5 ECC
Disk500 GB NVMe SSD2 TB NVMe SSD4+ TB NVMe (RAID 10)
Network1 Gbps10 Gbps25 / 40 / 100 Gbps
OSDebian 13 server (Trixie, minimal install), x86_64
  • eBPF/XDP: Debian 13 already ships a 6.x kernel. Use a NIC with native XDP support (Mellanox or Intel).
  • ClickHouse: from the Medium size up, move it to a dedicated server.
  • Retention: size the disk for the retention period you want; compression is around 10:1.
  • High availability: for the Enterprise size, a cluster of 2 or more nodes is recommended.

Ports used

PortProtocolService
2055UDPNetFlow v5/v9/IPFIX collector
6343UDPsFlow collector
11019TCPBMP station (optional)
3099TCPWeb dashboard
8123 / 9000TCPClickHouse (local only)
6379TCPRedis (local only)
50051TCPGoBGP gRPC (local only)
179TCPBGP (GoBGP ↔ router)
8090TCPScrubber /health (distributed installs only; open it to the analyzer alone)
80 / 443TCPProxy for domain access with HTTPS (optional, Step 10)

have this ready before you start The dashboard asks for the license key on first login, before any configuration. Without it, you install everything and get stuck on the activation screen. Before you open a terminal, have ready:

  • License key: sent when you subscribe. It is also available in the Customer area. Not a customer yet? See the plans.
  • How the dashboard will be reached: by IP or by a domain with HTTPS. This choice goes into the configuration as early as Step 7.3, and changing it later means reconfiguring. If you go with a domain, its DNS must already point to the server.
  • Two strong passwords: one for ClickHouse and one for MariaDB. Write both down, because each is used in more than one step.
Every command in this guide assumes you are running as root. If you are using a regular user, prefix them with sudo. Replace SUA_SENHA_CH and SUA_SENHA_MYSQL with the passwords you set aside.
  1. Prepare the system

    Update Debian and install the base tools. EdgeWarden ships as a prebuilt binary, so no build toolchain is needed:

    bash
    apt update && apt upgrade -y
    apt install -y curl sudo wget gnupg ca-certificates unzip \
      net-tools libpcap0.8 tcpdump htop \
      snmp                        # net-snmp: required for SNMPv3

    libpcap0.8 is the only system library the collector uses at runtime. No compilers, no PHP, no extra packages: the smaller the install, the smaller the attack surface.

    The snmp package (net-snmp) is only used if you add devices with SNMPv3. The collector has SNMP v1/v2c built in, but for v3 it calls the system's snmpbulkwalk. Installing it now costs a few KB and avoids a trap: without that binary, v3 polling fails silently, no counters show up and the log doesn't point to the cause.

    Kernel tuning (sysctl) for the collector

    The collector takes heavy UDP bursts (NetFlow/sFlow), and Debian's defaults are conservative. The core requests a 16 MB buffer per socket, but the stock net.core.rmem_max is only 208 KB. The request gets truncated, the kernel silently drops flow packets and traffic vanishes from the graphs with no visible error. Apply this once:

    bash · /etc/sysctl.d/99-flowspec-tuning.conf
    cat > /etc/sysctl.d/99-flowspec-tuning.conf <<'EOF'
    # Network buffers — NetFlow/sFlow collector (UDP bursts)
    # These are CEILINGS, not allocations: the kernel only reserves what the socket
    # asks for. The core asks for 16 MB per socket; the ceiling is generous on purpose.
    net.core.rmem_default = 31457280
    net.core.rmem_max = 134217728
    net.core.wmem_default = 31457280
    net.core.wmem_max = 134217728
    net.core.netdev_max_backlog = 250000
    net.core.optmem_max = 33554432
    net.core.somaxconn = 65535
    net.core.default_qdisc = fq
    
    # TCP — local ClickHouse + dashboard
    net.ipv4.tcp_rmem = 4096 87380 134217728
    net.ipv4.tcp_wmem = 4096 65536 134217728
    net.ipv4.tcp_max_syn_backlog = 65536
    net.ipv4.tcp_mtu_probing = 1
    net.ipv4.tcp_no_metrics_save = 1
    net.ipv4.ip_local_port_range = 1024 65535
    
    # File/thread limits — core + ClickHouse
    # SYSTEM-wide ceiling. This is NOT the per-process limit — see the note below.
    fs.file-max = 3263776
    fs.aio-max-nr = 3263776
    fs.pipe-max-size = 8388608
    kernel.threads-max = 1031306
    kernel.pid_max = 262144
    
    # Memory — ClickHouse leans hard on page cache; swapping kills latency
    vm.swappiness = 1
    vm.vfs_cache_pressure = 50
    # 256 MB is the cap the kernel itself uses when deriving this value from RAM.
    # The old 32768 sat BELOW the default on machines with 64 GB or more.
    vm.min_free_kbytes = 262144
    
    # Automatic reboot 3s after a kernel panic (unattended appliance)
    kernel.panic = 3
    EOF
    
    # Apply and check
    sysctl --system 2>&1 | grep -iE "error|cannot|no such" || echo "OK sem erros"
    sysctl net.core.rmem_max net.core.netdev_max_backlog vm.min_free_kbytes

    Creating the file now is right. Just don't come back later to change values in it.

    The same file ships with the package (scripts/sysctl/99-flowspec-tuning.conf), and from the first upgrade on, update.sh rewrites it on every update. A value edited directly in /etc/sysctl.d/99-flowspec-tuning.conf reverts to the original on the next upgrade, with no error and no warning. It's the kind of thing you find out too late.

    To change a value permanently, put only what you want different in a separate file with the zz- prefix:

    bash
    echo 'vm.swappiness = 10' > /etc/sysctl.d/99-zz-meus-ajustes.conf
    sysctl --system

    This works because sysctl.d reads files in alphabetical order and whoever sets a key last wins: 99-zz-meus-ajustes comes after 99-flowspec-tuning. That file is yours, and no upgrade touches it.

    nothing to do now Just so you know: fs.file-max is not the per-process limit. It's the ceiling for the whole system. Each service is still bound by systemd's LimitNOFILE, which defaults to just 1024 descriptors. EdgeWarden's units already set LimitNOFILE=65535, so there is nothing to tune. You'll check this and the socket buffer in Step 8, once the services exist.

    only if you run your own firewall Stateful firewall on this machine? On a clean Debian install with no firewall rules of your own, skip this note. EdgeWarden doesn't use conntrack: its nftables chain hooks in at hook input priority -150, ahead of connection tracking, and XDP acts before all of netfilter.

    If you do maintain your own stateful firewall, add an exemption for the flow ports to its ruleset: udp dport { 2055, 6343 } notrack. This is not a shell command; it's an nftables rule fragment. On a machine that does scrubbing, exempt transit traffic as well: conntrack creates state per flow, and a spoofed-source flood fills the table in seconds. It's the classic way to take down your own scrubber. Only raise net.nf_conntrack_max if you really need state, and in that case keep nf_conntrack_buckets at about ¼ of that value; otherwise the hash chains become the bottleneck.

    almost never applies Will this machine forward attack traffic? Only if it's an inline eBPF/XDP filter or a clean pipe scrubber. In the standard install, the server only receives NetFlow/sFlow samples and announces FlowSpec over BGP; the router is what drops the attack. If that's your setup, carry on as normal.

    If it will forward traffic, the data plane tuning is in Step 11, at the end of the guide. It depends on the installed package, so it can't be done here.

    Time zone and NTP

    Accurate timestamps are essential for the flow graphs and for license validation:

    bash
    timedatectl set-timezone America/Sao_Paulo   # set to your region
    apt install -y systemd-timesyncd
    printf '[Time]\nNTP=200.160.0.8\nFallbackNTP=a.ntp.br\n' > /etc/systemd/timesyncd.conf
    timedatectl set-ntp true
    timedatectl status   # "System clock synchronized: yes"
  2. Install Node.js 22

    The web dashboard runs on Node.js, installed from the official NodeSource repository:

    bash
    curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
    apt install -y nodejs
    
    node --version   # should print v22.x
  3. Install ClickHouse

    ClickHouse is the time-series database that stores the flows. Install it from the official repository:

    bash
    install -m 0755 -d /etc/apt/keyrings
    
    curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | \
        gpg --dearmor -o /etc/apt/keyrings/clickhouse.gpg
    
    echo "deb [signed-by=/etc/apt/keyrings/clickhouse.gpg] https://packages.clickhouse.com/deb stable main" | \
        tee /etc/apt/sources.list.d/clickhouse.list
    
    apt update
    apt install -y clickhouse-server clickhouse-client
    During installation, ClickHouse asks for the password of the default user. Set a strong password and write it down: this guide refers to it as SUA_SENHA_CH.

    Enable the service and check that it responds:

    bash
    systemctl enable --now clickhouse-server
    
    curl http://localhost:8123/ping
    Ok.
  4. Install MariaDB and Redis

    MariaDB stores the dashboard's users and settings; Redis distributes alerts in real time.

    bash
    apt install -y mariadb-server mariadb-client redis-server
    
    systemctl enable --now mariadb redis-server
    
    redis-cli ping
    PONG

    Set the MariaDB root password (replace SUA_SENHA_MYSQL):

    bash
    mariadb -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'SUA_SENHA_MYSQL'; FLUSH PRIVILEGES;"
  5. Install GoBGP mitigation

    GoBGP is the BGP daemon that announces mitigation rules (FlowSpec/RTBH) to your router:

    bash
    apt install -y gobgpd

    just install it for now Installing is enough to follow the guide. Peers are configured later, with the system running, and the file is never edited by hand: the generator reads the routers you add under Settings → Devices (Configurações → Dispositivos) and builds the configuration.

    When the time comes, run these in order. In gobgp neighbor, the session must show as Establ:

    bash · after adding the routers
    python3 /opt/flowspec-analyzer/scripts/gobgp/gen_config.py > /etc/gobgpd.conf
    systemctl enable --now gobgpd && systemctl restart gobgpd
    gobgp neighbor

    Note the >: the generator writes to standard output. Without the redirect, it only prints to the screen and /etc/gobgpd.conf stays empty. GoBGP starts with no peers and mitigation never announces anything.

    Not using automatic mitigation yet? You can leave this for later: collection, dashboard and detection all work without BGP.

  6. Download and extract the package

    Download the stable release and extract it to /opt:

    bash
    cd /opt
    curl -fLO https://www.edgewarden.com.br/download/flowspec-analyzer-stable.tar.gz
    
    tar -xzf flowspec-analyzer-stable.tar.gz -C /opt/
    cd /opt/flowspec-analyzer
    mkdir -p logs

    Create the license directory:

    bash
    mkdir -p /etc/flowspec-analyzer
    chown -R www-data:www-data /etc/flowspec-analyzer
    If the download or extraction fails (incomplete or corrupted file), download it again or contact support before moving on.
  7. Configure and create the databases

    7.1ClickHouse password

    Edit config.toml and enter the ClickHouse password you set in Step 3:

    bash
    nano /opt/flowspec-analyzer/config.toml
    # in the [storage.clickhouse] section:
    #   password = "SUA_SENHA_CH"

    7.2Databases, schema and seed data

    Create the database and load the schema and seed data (detection templates and decoders):

    bash
    cd /opt/flowspec-analyzer
    
    clickhouse-client --password 'SUA_SENHA_CH' -q "CREATE DATABASE IF NOT EXISTS flowspec"
    
    clickhouse-client --password 'SUA_SENHA_CH' --database flowspec --multiquery < database/clickhouse/schema.sql
    clickhouse-client --password 'SUA_SENHA_CH' --database flowspec --multiquery < database/clickhouse/seed.sql
    
    # authentication database (creates the default admin user)
    mysql -u root -p'SUA_SENHA_MYSQL' < database/mysql/auth_schema.sql

    7.3Dashboard environment and AUTH_URL

    Before running the command, decide which address the dashboard will be reached at. This is the choice that breaks the most installs, and it's made here:

    decide now AUTH_URL must be exactly the URL you will type into the browser: scheme, host and port. The dashboard runs in standalone mode, where Next builds the origin from this variable and ignores the Host header. There's no way for it to guess.

    If the value doesn't match the URL in use, login fails for every credential, the form looks stuck and the screen shows no useful error. Running setup-env.sh without AUTH_URL writes a placeholder value, and then login doesn't work at all.

    Option A

    Domain with HTTPS (recommended for production)

    Use the domain right here, even if Apache isn't set up yet:

    bash
    AUTH_URL=https://flowspec.example.net \
      MYSQL_USER=root MYSQL_PASSWORD='SUA_SENHA_MYSQL' ./scripts/setup-env.sh
    ✅ Gerado: frontend/.env.production ... AUTH_URL: https://flowspec.example.net ✓
    Went with A? Login will only work through the domain. Finish Step 8, jump to Step 10 (Apache and certificate), and only then come back to Step 9 for the first login. The domain's DNS must already point to this server.
    Option B

    Access by IP (internal network, or DNS not propagated yet)

    bash
    IP=$(ip -4 -br addr show | awk '$1!="lo"{print $3}' | cut -d/ -f1 | head -1)
    echo "Detectado: $IP"   # check it before moving on
    
    AUTH_URL=http://$IP:3099 \
      MYSQL_USER=root MYSQL_PASSWORD='SUA_SENHA_MYSQL' ./scripts/setup-env.sh
    Went with B? Follow the guide in its normal order. If you add a domain and HTTPS later, Step 10 shows how to switch AUTH_URL. Without that switch, login stops working when the address changes.

    Already ran it without AUTH_URL? No need to redo anything. Fix the line and restart the dashboard:

    bash
    grep AUTH_URL /opt/flowspec-analyzer/frontend/.env.production
    sed -i 's|^AUTH_URL=.*|AUTH_URL=https://flowspec.example.net|' /opt/flowspec-analyzer/frontend/.env.production
    systemctl restart flowspec-frontend

    7.4Public ASN data recommended

    Import the public ASN data. It powers the Peering, CDN and AS Analysis (Análise de AS) screens:

    bash
    CH_PASS='SUA_SENHA_CH' ./scripts/import-asn-names.sh http://127.0.0.1:8123
    CH_PASS='SUA_SENHA_CH' ./scripts/import-ip2asn.sh http://127.0.0.1:8123
    CH_PASS='SUA_SENHA_CH' ./scripts/seed-cdn-providers.sh http://127.0.0.1:8123
  8. Enable the services (systemd)

    The system runs as two supervised services: if a process dies, systemd restarts it on its own.

    bash
    cd /opt/flowspec-analyzer
    
    # correct ownership BEFORE starting (the services run as www-data)
    chown -R www-data:www-data /opt/flowspec-analyzer
    
    cp scripts/systemd/flowspec-core.service \
       scripts/systemd/flowspec-frontend.service \
       scripts/systemd/flowspec-analyzer.target /etc/systemd/system/
    
    # Reinstall the Step 1 tuning from the package so the file is identical
    # to what update.sh will maintain from now on (prevents drift).
    cp scripts/sysctl/99-flowspec-tuning.conf /etc/sysctl.d/ && sysctl --system
    
    systemctl daemon-reload
    systemctl enable --now flowspec-core flowspec-frontend flowspec-analyzer.target
    
    systemctl status flowspec-core flowspec-frontend
    ● flowspec-core.service — Active: active (running)
    ● flowspec-frontend.service — Active: active (running)

    8.1Check the tuning on the running process

    With the services up, confirm that the Step 1 tuning actually reached the process. These are three read-only checks; none of them changes anything:

    bash
    # 1. Core file descriptors — must read 65535, not 1024
    cat /proc/$(pgrep -f flowspec-analyzer-core)/limits | grep "open files"
    
    # 2. Collector socket buffer. The core asks for 16 MB and the kernel DOUBLES
    #    the value, so 32 MB is the right number: rb33554432.
    #    If you see rb425984, the Step 1 sysctl was not applied.
    ss -ulnpme | grep -A1 -E "2055|6343"
    grep "Socket buffer" /opt/flowspec-analyzer/logs/core.log.*
    Socket buffer: solicitado 16MB, obtido 32MB
    
    # 3. UDP drops — these counters must NOT grow over time
    nstat -az | grep -iE "Udp.*(InErrors|RcvbufErrors)"
    Upgrading an older install? If item 1 still shows 1024, the unit in use is from the previous release. Copy it again from scripts/systemd/ (command above) and run systemctl daemon-reload followed by systemctl restart flowspec-analyzer.target. Without daemon-reload, the new limit doesn't take effect, even with the file in place.

    Everyday commands

    • Restart everythingsystemctl restart flowspec-analyzer.target
    • Restart the dashboard onlysystemctl restart flowspec-frontend
    • Follow the logsjournalctl -u flowspec-core -f
  9. Firewall and first login

    9.1Open the ports

    Open only what's needed (example with ufw):

    bash
    apt install -y ufw
    ufw allow ssh
    ufw allow 3099/tcp    # dashboard
    ufw allow 2055/udp    # NetFlow
    ufw allow 6343/udp    # sFlow
    ufw enable
    ufw status

    9.2First login

    Open the dashboard and log in for the first time. Use exactly the URL saved in AUTH_URL in Step 7.3: the IP if you chose option B; the domain if you chose A (in which case Step 10 must be done first).

    URL
    the same as your AUTH_URL
    User
    flowspec@managerpro.networkor admin@edgewarden.com.br
    Password
    admin123(both accounts)
    On first login, change the email and password of both default admin accounts (or delete the one you don't use) under Settings → Users (Configurações → Usuários) (after changing the email, log out and back in). Then create your team's users with the right roles: admin, operator or viewer.

    9.3Activate the license

    On the first screen, the dashboard asks for the license key you received when you subscribed. Enter the key and restart the core to complete activation:

    bash
    systemctl restart flowspec-core
    Your license key, subscription status and invoices are in the Customer area: license.managerpro.dev.br. Your login is created when you subscribe and sent to your email.

    9.4Point your routers at the collector

    On your routers, configure NetFlow v9/IPFIX export (port 2055/UDP) or sFlow (port 6343/UDP) to the server's IP, and add each device under Settings → Devices. Within 1 to 2 minutes, flows start showing up on the dashboard.

    Installation complete. The dashboard is already showing traffic in real time. Next, explore IP Zones (Zonas de IP) and Threshold Templates (Templates de Threshold) to tune DDoS detection to your network.
  10. Domain access and HTTPS optional

    The dashboard already works at http://IP:3099. If you want a friendly address with a certificate (for example, flowspec.example.net), put Apache in front as a reverse proxy.

    10.1Apache and certbot

    Install Apache and certbot and enable the modules:

    bash
    apt install -y apache2 certbot python3-certbot-apache
    a2enmod proxy proxy_http proxy_wstunnel ssl headers rewrite
    systemctl restart apache2

    10.2VirtualHost

    Create the VirtualHost, replacing the domain with yours. The DNS must already point to the server:

    /etc/apache2/sites-available/flowspec.conf
    <VirtualHost *:80>
        ServerName flowspec.example.net
    
        ProxyPreserveHost On
        ProxyRequests Off
    
        # WebSocket (real-time dashboard updates)
        RewriteEngine On
        RewriteCond %{HTTP:Upgrade} =websocket [NC]
        RewriteRule /(.*) ws://127.0.0.1:3099/$1 [P,L]
    
        ProxyPass        / http://127.0.0.1:3099/
        ProxyPassReverse / http://127.0.0.1:3099/
    
        # IMPORTANT: never set X-Forwarded-Host manually — Apache already
        # sends that header on its own, and duplicating it breaks login (Invalid URL).
        RequestHeader unset X-Forwarded-Host
        # Dynamic scheme (http/https) — stays correct when certbot
        # clones this vhost to port 443:
        RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
    
        ErrorLog  ${APACHE_LOG_DIR}/flowspec-error.log
        CustomLog ${APACHE_LOG_DIR}/flowspec-access.log combined
    </VirtualHost>

    10.3Site live and certificate

    Enable the site and issue the certificate. certbot creates the HTTPS vhost and the redirect automatically:

    bash
    # 1. Open 80 and 443 BEFORE certbot: it validates the domain over HTTP on
    #    port 80, and the Step 9 ufw rules didn't open it. Out of order, it fails.
    ufw allow 80/tcp
    ufw allow 443/tcp
    
    # 2. Enable the site
    a2ensite flowspec
    apachectl configtest && systemctl reload apache2
    
    # 3. Issue the certificate (creates the HTTPS vhost and the redirect automatically)
    certbot --apache -d flowspec.example.net
    
    # 4. AUTH_URL becomes the domain. Without this, login stops working
    #    the moment you switch from the IP to the domain in the browser.
    sed -i 's|^AUTH_URL=.*|AUTH_URL=https://flowspec.example.net|' \
      /opt/flowspec-analyzer/frontend/.env.production
    systemctl restart flowspec-frontend
    
    # 5. Only AFTER login through the domain works (optional):
    #    ufw deny 3099/tcp    # forces access through the domain/HTTPS only

    Don't close port 3099 too early. Until login through the domain is proven, it's your way back in if something in Apache or the certificate fails. Leave ufw deny 3099/tcp for last, and only run it if you really want to block direct access.

    If you came from option A in Step 7.3, AUTH_URL is already the domain: item 4 changes nothing and is safe to run anyway.

    Validate. The response should be 307, redirecting to the login page on your domain:

    bash
    curl -sk -o /dev/null -w "%{http_code} -> %{redirect_url}\n" https://flowspec.example.net/
    307 -> https://flowspec.example.net/login
  11. Data plane: Packet Filter (XDP) and clean pipe optional

    The Step 1 tuning targets the collector, which only receives flow samples. If this machine also forwards packets (inline eBPF/XDP filtering or diversion to clean pipe scrubbing), it needs a second set of keys aimed at the data plane: forwarding, softirq, qdisc and the eBPF JIT. That's what determines how many Gbps and Mpps the appliance handles on your hardware; the collector's socket buffer plays no part here.

    This step sits at the end of the guide on purpose: part of it uses files that only exist after installation (scripts/sysctl/ and deploy/scrubber/), and the scrubbing path only makes sense with the system already up.

    stop and decide This entire step is optional, and most installs skip it completely.

    It only applies if this machine forwards attack traffic: if it's an inline eBPF/XDP filter or the scrubber in a clean pipe (the victim's traffic goes in and out through it).

    In a standard EdgeWarden install, the server only receives samples of NetFlow/sFlow and announces FlowSpec over BGP; the router is what drops the attack. If that's your setup, the installation ended at Step 9 and you can close this guide.

    Data plane tuning

    safe on any machine This is performance only. ip_forward, rp_filter and ICMP redirects are not in this file, on purpose: they're handled by scrubber-path.sh, which validates the topology before enabling anything and knows how to roll back. This block is safe on any machine and only makes a difference where packets pass through.
    bash
    # The file ships ready-made in the package. Copying instead of typing ensures
    # it never drifts from what the product tests — and it's worth reading first:
    # every key has the reasoning commented next to it.
    cat /opt/flowspec-analyzer/scripts/sysctl/99-zz-flowspec-dataplane.conf.example
    
    # The "zz" suffix takes precedence over the Step 1 99-flowspec-tuning:
    # sysctl.d applies files alphabetically and the last one to set a key wins.
    cp /opt/flowspec-analyzer/scripts/sysctl/99-zz-flowspec-dataplane.conf.example \
       /etc/sysctl.d/99-zz-flowspec-dataplane.conf
    
    sysctl --system 2>&1 | grep -iE "error|cannot|no such" || echo "OK sem erros"
    sysctl net.core.netdev_budget net.core.default_qdisc net.core.bpf_jit_harden

    Forwarding (clean pipe)

    This part is not done by hand. Turning on ip_forward in a sysctl file makes the appliance a router without validating anything. scrubber-path.sh checks the topology first (the router must be on the same L2 segment), applies the keys with the care a hairpin requires and knows how to roll back:

    bash
    cd /opt/flowspec-analyzer/deploy/scrubber
    cp scrubber-path.conf.example scrubber-path.conf
    $EDITOR scrubber-path.conf              # DIRTY_IF, ROUTER_IP, PROTECT_PREFIXES
    
    ./scrubber-path.sh check                # validates, touches nothing
    ./scrubber-path.sh up                   # brings it up — runtime only
    ./scrubber-path.sh verify 203.0.113.10  # simulates the decision for one victim
    
    # Only AFTER verify passes, make it permanent:
    ./scrubber-path.sh up --persist
    ./scrubber-path.sh status               # should say "Persistência ativa"

    Don't skip --persist. Without it, the path lives only in memory, and the failure mode is the worst possible: the machine reboots, the core comes up, XDP loads, the router keeps diverting the victim here, and the appliance drops every packet because it's no longer forwarding. Every service shows active (running) and the dashboard flags nothing. Your customer goes offline during the attack.

    --persist is also not the default on purpose: an unproven path coming up on its own at boot is worse than a path that disappears. That's why the order is prove first, persist after, and status warns you while persistence is missing.

    NIC tuning

    No sysctl makes up for a badly configured NIC. Above 100 Gbps, this is where it's decided: the commands below buy more pps than the entire previous block.

    maintenance window Don't paste this whole block on a production machine. Items 1 and 2 (ethtool -G and ethtool -L) reset the NIC: the link goes down and comes back, and anything carrying customer traffic sees a one-to-two-second outage. Item 4 disables a system service. Run it in an agreed window, one item at a time, checking between them.

    And change IF=eth0 to your real interface before anything else (ip -br link lists the available ones). Pointing at the wrong interface reconfigures the wrong NIC.

    bash
    IF=eth0   # data plane interface (the one receiving dirty traffic)
    
    # 1. Ring buffers at the MAXIMUM the NIC supports (read from the NIC itself)
    RXMAX=$(ethtool -g $IF | awk '/^RX:/{print $2; exit}')
    TXMAX=$(ethtool -g $IF | awk '/^TX:/{print $2; exit}')
    ethtool -G $IF rx $RXMAX tx $TXMAX
    
    # 2. RSS queues — one per core on the SAME NUMA node as the NIC (see the note)
    NODE=$(cat /sys/class/net/$IF/device/numa_node)
    NCPU=$(lscpu -p=CPU,NODE | grep -c ",$NODE$")
    ethtool -L $IF combined $NCPU
    
    # 3. Adaptive coalescing: fewer IRQs per packet under bursts
    ethtool -C $IF adaptive-rx on adaptive-tx on
    
    # 4. Pinned IRQ per queue — irqbalance reshuffles exactly when under load
    systemctl disable --now irqbalance
    
    # 5. Which mode did XDP come up in?
    ip -d link show $IF | grep -i xdp
    #    "xdp"        = native, in the driver — required at this scale
    #    "xdpgeneric" = SKB fallback, runs AFTER GRO. In this mode the filter
    #                   sees already-coalesced packets and undercounts pps, so:
    ethtool -K $IF gro off lro off
    
    # 6. Check whether the NIC is dropping packets (everything should stay at zero)
    ethtool -S $IF | grep -iE "drop|discard|miss|nobuf|error" | grep -v ": 0$"

    NUMA matters more than core count. On a dual-socket machine, an RSS queue served by a core on the wrong node pays the interconnect crossing on every packet. If cat /sys/class/net/$IF/device/numa_node returns 0, use only node 0's cores: 16 queues on the right node outperform 64 spread across both.

    Also confirm the slot width: 200 Gbps requires PCIe 4.0 x16 (lspci -vv -s $(basename $(readlink /sys/class/net/$IF/device)) | grep -i "LnkSta:"). In an x8 slot, the NIC never delivers its nominal rate, and no amount of tuning fixes that.

    ethtool settings don't survive a reboot. Pin them in a post-up in /etc/network/interfaces or in a systemd oneshot unit with After=network-online.target, with the same care that applies to sysctl.d.
after installation

Upgrade to new releases

Upgrading is one command. The updater checks the package's integrity, keeps your settings, swaps the release and runs a health check. Downtime is usually under 1 minute, and historical data is not touched:

bash
cd /opt
curl -fLO https://www.edgewarden.com.br/download/flowspec-analyzer-stable.tar.gz

/opt/flowspec-analyzer/scripts/update.sh /opt/flowspec-analyzer-stable.tar.gz

Running scrubbing or clean pipe? For this upgrade only, back up the topology first. deploy/scrubber/scrubber-path.conf describes your network's topology, so it isn't in the package, and the updater replaces the whole directory. The new update.sh already preserves that file, but the swap is performed by the updater already on the server, which is still the old one. From the next upgrade on, it's automatic.

  • Before: cp /opt/flowspec-analyzer/deploy/scrubber/scrubber-path.conf /root/
  • After: cp /root/scrubber-path.conf /opt/flowspec-analyzer/deploy/scrubber/

If you forget, the file is still in /opt/flowspec-analyzer.previous/deploy/scrubber/. After restoring it, run ./scrubber-path.sh status to confirm the path came back intact.

If something doesn't go as expected, rolling back to the previous release is also one command:

bash
/opt/flowspec-analyzer/scripts/update.sh --rollback
diagnostics

Troubleshooting

SymptomFix
The dashboard won't open or the service won't start journalctl -u flowspec-frontend -n 50. The most common cause is permissions: run chown -R www-data:www-data /opt/flowspec-analyzer and systemctl restart flowspec-analyzer.target.
Login returns error=Configuration The dashboard couldn't connect to MariaDB. Check the user and password and run it again: MYSQL_USER=root MYSQL_PASSWORD='...' ./scripts/setup-env.sh --force
No flows show up Check whether packets are arriving: tcpdump -i any -n udp port 2055. If they aren't, the problem is in the router's export config or the firewall; if they are, check journalctl -u flowspec-core -f.
ClickHouse doesn't respond systemctl status clickhouse-server and curl http://localhost:8123/ping. The most common cause is a wrong password in config.toml.
Domain access redirects to the wrong place Behind a reverse proxy, preserve the Host header: in Apache, use ProxyPreserveHost On and do not set X-Forwarded-Host manually; in nginx, use proxy_set_header Host $host;.
SNMPv3 device with no counters net-snmp is missing: the collector has v1/v2c built in, but calls the system's snmpbulkwalk for v3. Check with command -v snmpbulkwalk and install with apt install -y snmp. For v2c, the package isn't needed.
Scrubbing worked, the machine rebooted and it stopped The path was brought up without persistence. ./scrubber-path.sh status lists the keys that reverted to default and the missing nftables table; ./scrubber-path.sh up --persist fixes it and makes the path survive future reboots.
A sysctl setting disappears after an upgrade Edited /etc/sysctl.d/99-flowspec-tuning.conf by hand? The updater reinstalls it on every upgrade. Put your values in a separate file with the zz- prefix, which is applied later and wins.
support

Stuck on a step?

Reach support on WhatsApp or by email. During your installation window, we stay with you until the dashboard is up.

Support in Portuguese, Monday to Friday, 8 a.m. to 6 p.m. (Brasília time).

Don't have a license yet?

Compare the plans or book a demo using data from your own network.