Skip to main content

Installation

WaterWall is distributed as a standalone executable. You do not need a package manager service, a database, or a separate runtime. Download the build for your operating system, place a core.json beside the executable, and run it from that directory.

Most deployments run WaterWall on a Linux VPS. You can manage the files from a Windows machine over SSH or VS Code Remote SSH, but the commands below are meant to be executed on the server.

Download​

Download the latest release from the WaterWall release page and choose the asset that matches your operating system and CPU architecture.

Linux x64 builds are packed executables under their existing download names, including the old-CPU and AVX512 builds. Run the executable normally; it restores WaterWall in memory at startup without extracting a temporary executable to disk. These builds require Linux 3.19 or newer with memfd_create, file sealing, execveat, permission to execute anonymous memory-backed files, and readable /proc/self/exe. The selected build's CPU and system-library requirements still apply. Set-ID and file-capability deployment are not supported by the packed loader.

Windows downloads retain ordinary executables. Optional source builds with WW_PACK_RUNTIME=ON use a private temporary executable and a separate child process, while the launcher waits and cleans up. This needs native Windows acceptance before release use. Keep required companion DLLs beside the launcher. Firewall rules and service wrappers that identify a stable executable path/PID must account for the temporary child; a rule for the outer executable is not sufficient. Forced termination may leave temporary files. Windows ARM packing is unsupported.

On Linux, a typical manual install looks like this:

mkdir -p ~/waterwall
cd ~/waterwall

# Put the downloaded archive or binary here, then extract it if needed.
chmod +x ./WaterWall

If the release asset is compressed, extract it first with the matching tool, for example tar, unzip, or 7z.

Directory Layout​

A small deployment usually looks like this:

waterwall/
WaterWall
core.json
configs/
local-test.json
logs/

By default, WaterWall reads ./core.json from the process working directory. The paths inside core.json, including the files listed in configs, are passed to the loader as written, so keep your working directory intentional.

Selecting Another Core Input​

Use any of the equivalent forms -c:PATH, --c:PATH, -config:PATH, --config:PATH, or config:PATH. The path may be relative or absolute. Use stdin to pipe the JSON directly:

./WaterWall --config:/etc/waterwall/core.json
./WaterWall --config:stdin < core.json

You may instead set WW_CORE_JSON_INPUT to a path or stdin. A CLI option has priority over the environment variable, and the environment variable has priority over the default ./core.json.

The selected source does not change the working directory. Relative paths inside the JSON remain relative to the directory where WaterWall was started.

Minimal core.json​

Create core.json beside the executable:

{
"log": {
"path": "logs/",
"core": {
"loglevel": "INFO",
"file": "core.log",
"console": true
},
"network": {
"loglevel": "INFO",
"file": "network.log",
"console": true
},
"dns": {
"loglevel": "INFO",
"file": "dns.log",
"console": true
},
"internal": {
"loglevel": "INFO",
"file": "internal.log",
"console": true
}
},
"misc": {
"workers": 1,
"ram-profile": "client",
"mtu": 1500,
"try-enabling-bbr": true,
"libs-path": "libs/"
},
"dns": {
"domain-strategy": "prefer-ipv4"
},
"configs": [
"configs/local-test.json"
]
}

The configs array is required and must contain at least one config file path. Other sections are optional, but keeping misc explicit makes the runtime easier to reason about.

Minimal Test Chain​

Create configs/local-test.json:

{
"name": "local-test",
"author": "waterwall-user",
"config-version": 1,
"core-minimum-version": 0,
"nodes": [
{
"name": "listen-http",
"type": "TcpListener",
"settings": {
"address": "127.0.0.1",
"port": 8080
},
"next": "connect-example"
},
{
"name": "connect-example",
"type": "TcpConnector",
"settings": {
"address": "example.com",
"port": 80
}
}
]
}

Run WaterWall:

cd ~/waterwall
./WaterWall

In another terminal, test the local listener:

curl -v http://127.0.0.1:8080/

If the request reaches example.com, the binary, core file, config loader, TCP listener, DNS resolver, and TCP connector are all working.

Running on a VPS​

For a public service, bind listeners to a public or wildcard address:

"address": "0.0.0.0"

Then allow the port in your VPS firewall or cloud firewall. For example, if you listen on port 443, the operating system and provider firewall must both allow inbound TCP 443.

Ports below 1024 usually require root privileges or a capability such as CAP_NET_BIND_SERVICE. Packet-level nodes such as TunDevice may also require extra privileges because they create or use network interfaces.

Common Problems​

core.json not found: Make sure you run WaterWall from the directory that contains core.json, select it with --config:PATH or WW_CORE_JSON_INPUT, or use your service manager's working-directory option.

Config file not found: Check every path in configs. Relative paths are normally relative to the current working directory.

Permission denied: Run chmod +x ./WaterWall. If the listener port is below 1024, run with the needed privilege or choose a higher port for testing.

Port already in use: Use ss -ltnp on Linux to find the process already listening on that TCP port.

Remote clients cannot connect: Check the listener address, WaterWall logs, the Linux firewall, and the provider firewall. A listener bound to 127.0.0.1 only accepts local connections.

DNS resolution fails: Check the dns block in core.json, especially servers and domain-strategy, then look at logs/dns.log.