# RustScan User Guide

RustScan is the modern day port scanner — a fast, lightweight CLI written in Rust that finds open TCP (and optionally UDP) ports on a host or network in seconds, then hands the results off to Nmap (or any tool you choose) for deeper service and vulnerability analysis. It is published on crates.io as the `rustscan` binary at version **2.4.1** and is intended to be used as the speed layer in front of slower, smarter scanners.

This guide is for end users: people who want to scan ports, pipe results into Nmap, run scripts on discovered services, and understand every CLI flag without surprises.

---

## 1. Quick Start

The fastest way to scan a host with RustScan, with zero local install, is the official Docker image. It ships with a sane `ulimit`, the embedded port database, and the default Nmap-handoff script ready to go.

```bash
docker run -it --rm rustscan/rustscan:latest -a 127.0.0.1
```

```text
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
[~] The config file is expected to be at "/root/.config/.rustscan.toml"
Open 127.0.0.1:22
Open 127.0.0.1:631
[~] Starting Script(s)
```

That single command:

1. Pulls the `rustscan/rustscan` Docker image.
2. Scans every TCP port (1-65535) on `127.0.0.1`.
3. Pipes any ports it finds into Nmap with `-Pn -vvv -p $PORTS` for deeper inspection.

> [!TIP]
> If you only want the open ports — no Nmap, no banner, no color — add `-g` (greppable mode): `docker run -it --rm rustscan/rustscan:latest -g -a 127.0.0.1` prints just `127.0.0.1 -> [22,631]`, perfect for piping into another tool.

### Key features at a glance

- TCP connect scan over the entire 1-65535 range in seconds (default behaviour).
- Optional UDP probing with `--udp` and an embedded payload database for common UDP services.
- Three port strategies: full range (`-r`), explicit port list (`-p`), or top-1000 (`--top`) from the bundled `config.toml`.
- Two scan orders: `serial` (1, 2, 3, ...) or `random` (Linear Congruential Generator) to evade lazy IDS rules.
- Native Nmap handoff: positional arguments after `--` are forwarded into Nmap so you keep your existing workflow.
- A scripting engine that runs the embedded Nmap script by default, or your own custom scripts (`Default` / `Custom` / `None`).
- Accessible mode (`--accessible`) that drops ASCII art and color so screen readers work.
- Greppable mode (`-g`) for piping into other CLI tools.
- TOML config file at `~/.config/.rustscan.toml` for default flags.

### Prerequisites (single-line, native install)

A Rust toolchain (Cargo 1.x with Rust edition 2018+) for `cargo install`, or Docker, or Nmap on `$PATH` if you want the default script handoff to actually call Nmap.

### What to try next

- Section 2 — pick the install path that fits your environment.
- Section 4 — learn the flags you'll use every day.
- Section 5 — copy-paste recipes for the most common scans.

---

## 2. Installation

RustScan publishes prebuilt binaries on its GitHub releases page, a Docker image, and a crate on `crates.io`. Pick whichever fits your environment.

### 2.1 Docker (recommended — works everywhere)

```bash
docker pull rustscan/rustscan:latest
docker run -it --rm rustscan/rustscan:latest -a scanme.nmap.org
```

```text
Open 45.33.32.156:22
Open 45.33.32.156:80
Open 45.33.32.156:9929
Open 45.33.32.156:31337
[~] Starting Script(s)
```

> [!IMPORTANT]
> Only run RustScan against hosts you own or have explicit written permission to scan. Port scanning third-party infrastructure can violate the Computer Fraud and Abuse Act (US), the Computer Misuse Act (UK), and equivalent laws elsewhere. `scanme.nmap.org` is an exception — Nmap.org explicitly authorises scanning it for testing.

### 2.2 Cargo (Rust developers)

```bash
cargo install rustscan
```

This compiles RustScan **2.4.1** against your local Rust toolchain (edition 2018 minimum) and drops the `rustscan` binary into `$HOME/.cargo/bin/`. Make sure that directory is on your `$PATH`.

```bash
rustscan --version
```

```text
rustscan 2.4.1
```

> [!WARNING]
> Some users on Debian 12 have reported `cargo install` of 2.4.1 failing with an `env_logger` version mismatch (issue #824). If that affects you, fall back to the Docker image or the prebuilt binary.

### 2.3 Prebuilt release binary (no compiler, no Docker)

Download the appropriate archive for your OS/arch from the [GitHub releases page](https://github.com/RustScan/RustScan/releases), extract it, and move the `rustscan` binary somewhere on your `$PATH`:

```bash
curl -L https://github.com/RustScan/RustScan/releases/latest/download/rustscan_2.4.1_amd64.deb -o rustscan.deb
sudo dpkg -i rustscan.deb
```

The bundled `.deb` package declares `nmap` as a runtime dependency (`[package.metadata.deb] depends = "$auto, nmap"`), so apt will pull it in for you.

### 2.4 Build from source (contributors / patched builds)

```bash
git clone https://github.com/RustScan/RustScan.git
cd RustScan
cargo build --release
./target/release/rustscan --version
```

```text
rustscan 2.4.1
```

> [!NOTE]
> The build script (`build.rs`) reads the `nmap-payloads` file at the repo root and generates `src/generated.rs` containing the embedded UDP payload map. This step is automatic — if `cargo build` complains that `nmap-payloads` is missing, run `git submodule update --init` first.

### Platform support matrix

| Platform | Method | Notes |
|---|---|---|
| Linux x86_64 | Docker / .deb / cargo / source | Fully supported. `rlimit` raises file-descriptor limit automatically. |
| Linux ARM64 | Docker / cargo / source | Builds locally; CI ARM coverage is tracked in #683. |
| macOS | cargo / source | Fully supported; ulimit logic still applies. |
| Windows | Docker / cargo | Custom scripts on Windows have known issues (#513). Batch size falls back to a fixed `AVERAGE_BATCH_SIZE = 3000` because `rlimit` is Unix-only. |

### Post-install verification

```bash
rustscan -a 127.0.0.1 -r 1-100 --no-banner -g
```

```text
127.0.0.1 -> []
```

If you get a help banner, a `[~]` config-path notice, and either an open-port list or an empty `[]`, the install is healthy.

---

## 3. Configuration

RustScan is happy with zero config — every option has a sensible default. But for repeat scans you can drop a TOML file at `~/.config/.rustscan.toml` (or, on legacy installs, `~/.rustscan.toml`) and RustScan will merge those values with whatever you pass on the CLI.

### 3.1 Where the config file lives

| Path | When used |
|---|---|
| `$XDG_CONFIG_HOME/.rustscan.toml` (typically `~/.config/.rustscan.toml`) | Default location on Linux/macOS. |
| `~/.rustscan.toml` | Backwards-compatible legacy path; checked if the new path doesn't exist. |
| Whatever you pass with `-c PATH` / `--config-path PATH` | Custom path; takes precedence. |

The first time RustScan starts (without `--no-banner`) it prints both paths it checked, so you always know exactly where the file should be.

> [!NOTE]
> The legacy `~/.rustscan.toml` location predates XDG-compliance; community issue #741 tracks improving config-path handling. Use the new `~/.config/.rustscan.toml` path for new installs.

### 3.2 Annotated example config

```toml
# ~/.config/.rustscan.toml
addresses = ["127.0.0.1", "10.0.0.0/24"]   # Default targets if -a not given
ports = [80, 443, 8080]                     # Specific port list (omit to use range)
range = { start = 1, end = 1000 }           # Or a range — but ports + range conflict
greppable = false                           # Suppress banner / color when true
accessible = false                          # Drop ASCII art for screen readers
batch_size = 4500                           # Concurrent socket attempts
timeout = 1500                              # Per-port milliseconds before giving up
tries = 1                                   # Retries before declaring a port closed
ulimit = 5000                               # Auto-raise OS file-descriptor limit
scan_order = "Serial"                       # "Serial" or "Random"
scripts = "Default"                         # "None" / "Default" / "Custom"
command = ["-A"]                            # Extra Nmap flags appended after --
udp = false                                 # TCP-only by default
no_banner = false                           # Suppress the ASCII intro
exclude_ports = [9100]                      # Skip these even if in range
exclude_addresses = ["10.0.0.5"]            # Skip these IPs/CIDRs
```

> [!TIP]
> Anything you put in this file is overrideable on the CLI — pass `--no-config` to ignore the file completely for a one-off scan.

### 3.3 Common option combinations

| Goal | Config snippet |
|---|---|
| Quiet, machine-readable output | `greppable = true` |
| Fast scans on a beefy server | `batch_size = 8000`, `ulimit = 10000` |
| Slower, more reliable on lossy networks | `timeout = 3000`, `tries = 2` |
| Random order to evade naive IDS | `scan_order = "Random"` |
| Ports only, no Nmap handoff | `scripts = "None"` |

For the full per-key reference and merging rules, see the Admin Guide.

---

## 4. Core Usage

The complete CLI surface, in the order you'll actually use it.

### 4.1 Address selection

| Flag | Long form | Type | Description |
|---|---|---|---|
| `-a` | `--addresses` | comma list / file | IPs, hostnames, or CIDRs to scan. Newline-delimited file accepted. |
| `-x` | `--exclude-addresses` | comma list | Skip these IPs/CIDRs/hostnames. |

```bash
rustscan -a 192.168.1.1,192.168.1.2,scanme.nmap.org
rustscan -a 192.168.1.0/24 -x 192.168.1.1
rustscan -a hosts.txt           # one host per line
```

Hostnames are resolved via the system resolver (`/etc/resolv.conf` on Unix) first; if that fails, RustScan falls back to a Cloudflare DNS-over-TLS resolver. Override with `--resolver`.

### 4.2 Port selection

| Flag | Long form | Type | Default | Description |
|---|---|---|---|---|
| `-p` | `--ports` | comma list of u16 | unset | Explicit port list. Mutually exclusive with `-r`. |
| `-r` | `--range` | `START-END` | `1-65535` | Inclusive port range. |
| `-e` | `--exclude-ports` | comma list of u16 | unset | Ports to skip. |
| | `--top` | flag | `false` | Use the top-1000 ports baked into `config.toml`. |

```bash
rustscan -a 10.0.0.1 -p 22,80,443,8080
rustscan -a 10.0.0.1 -r 1-1024
rustscan -a 10.0.0.1 --top
```

> [!NOTE]
> If you supply neither `-p` nor `-r`, RustScan defaults to a full sweep `1-65535`. The `--top` flag only activates when you have a `ports = [...]` list in your config file — without that, `--top` is a no-op.

### 4.3 Speed and reliability

| Flag | Long form | Type | Default | Description |
|---|---|---|---|---|
| `-b` | `--batch-size` | usize | `4500` | Concurrent socket attempts. Bounded by your OS file-descriptor limit. |
| `-t` | `--timeout` | u32 ms | `1500` | Per-port wait before declaring it closed. |
| | `--tries` | u8 | `1` | Retries on transient errors. `0` is silently corrected to `1`. |
| `-u` | `--ulimit` | usize | unset | Raise the OS open-file-descriptor limit before scanning. |
| | `--scan-order` | `Serial`/`Random` | `Serial` | Order of port iteration. |

```bash
rustscan -a 10.0.0.0/24 -b 6000 -t 2000 --tries 2
rustscan -a 10.0.0.1 -u 8000 -b 7900     # raise ulimit, ride right under it
```

> [!WARNING]
> Setting `--batch-size` higher than your OS file-descriptor limit will cause RustScan to log `File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers` and silently lower the batch. Either pair `-b` with a matching `-u`, or trust the auto-tuner.

### 4.4 Output mode

| Flag | Long form | Default | Description |
|---|---|---|---|
| `-g` | `--greppable` | `false` | Print only `IP -> [PORTS]` lines. No banner, no color. Skips Nmap. |
| | `--accessible` | `false` | Strip ASCII art / color (screen-reader friendly). |
| | `--no-banner` | `false` | Hide the intro banner but keep the rest of the output. |

### 4.5 Scripting engine

| Flag | Type | Default | Description |
|---|---|---|---|
| `--scripts` | `None`/`Default`/`Custom` | `Default` | Picks the script-execution mode. |

- **`Default`** — runs the bundled Nmap script: it appends `-Pn -vvv -p $PORTS` and execs `nmap` against each open-port set.
- **`None`** — skip scripting entirely. Print results and exit.
- **`Custom`** — load TOML descriptors from `~/.rustscan_scripts/` plus `~/.rustscan_scripts.toml` and run them.

Anything after `--` on the command line is forwarded into the underlying script call. The config-merger ignores `--scripts` itself, so this flag is CLI-controlled per scan.

```bash
# Pass -A and -sC into Nmap
rustscan -t 1500 -a 127.0.0.1 -- -A -sC

# Pass an Nmap script category
rustscan -a 127.0.0.1 -- --script "'(safe and vuln)'"
```

### 4.6 Other flags

| Flag | Long form | Default | Description |
|---|---|---|---|
| `-c` | `--config-path` | XDG default | Custom config-file path. |
| `-n` | `--no-config` | `false` | Ignore the config file entirely for this run. |
| | `--no-banner` | `false` | Hide ASCII intro. |
| | `--resolver` | system | Comma-separated list of DNS resolver IPs, or a file with one per line. |
| | `--udp` | `false` | UDP probing instead of TCP connect. Uses the embedded `nmap-payloads` map. |

---

## 5. Common Tasks

Eight copy-paste recipes, ordered roughly by how often you'll need them.

### 5.1 Scan a single host across the entire TCP range

```bash
rustscan -a 192.0.2.10
```

Defaults to `-r 1-65535`, batch size 4500, 1500 ms timeout, serial order, then hands open ports to Nmap with `-Pn -vvv -p $PORTS`. Use this as your everyday baseline.

### 5.2 Scan a CIDR

```bash
rustscan -a 10.0.0.0/24 -r 1-1024 --scan-order random
```

The CIDR expands to every host address (including network/broadcast). Combine with `-x` to exclude management hosts:

```bash
rustscan -a 10.0.0.0/24 -x 10.0.0.1,10.0.0.254 -r 1-1024
```

> [!TIP]
> Random order shuffles ports inside the range. For multi-host scans the internal `SocketIterator` holds each port across all IPs before advancing — so when scanning `10.0.0.1` and `10.0.0.2` for ports 22 and 80, RustScan visits 10.0.0.1:22, 10.0.0.2:22, 10.0.0.1:80, 10.0.0.2:80. That distributes load instead of hammering one host at a time.

### 5.3 Scan a list of hosts from a file

`hosts.txt`:

```text
10.0.0.1
10.0.0.2
scanme.nmap.org
```

```bash
rustscan -a hosts.txt -p 22,80,443
```

Anything in `-a` that isn't a literal IP, hostname, or CIDR is treated as a file path. Lines that don't parse as addresses are silently skipped.

### 5.4 Pipe results into Nmap with custom flags

```bash
rustscan -a 192.0.2.10 -- -A -sC -oN nmap.txt
```

Everything after `--` is appended to the Nmap invocation that the default script runs. Equivalent to `nmap -Pn -vvv -p <discovered-ports> -A -sC -oN nmap.txt 192.0.2.10`.

```bash
# Service version detection only
rustscan -a 192.0.2.10 -- -sV

# Full vulnerability sweep
rustscan -a 192.0.2.10 -- --script vuln
```

> [!IMPORTANT]
> The default script requires `nmap` on `$PATH`. The Docker image ships with it; on a `cargo install` you need to install Nmap separately (`apt install nmap`, `brew install nmap`, etc.).

### 5.5 Greppable output for shell pipelines

```bash
rustscan -g -a 10.0.0.1 -r 1-1024
```

```text
10.0.0.1 -> [22,80,443]
```

Pair with `xargs`, `awk`, or `jq` to build automation:

```bash
rustscan -g -a 10.0.0.0/24 -r 22,80,443 \
  | awk -F' -> ' '$2 != "[]" { print $1 }'
```

This emits only IPs that have at least one of the ports open.

### 5.6 Use a custom script

Drop a TOML descriptor at `~/.rustscan_scripts/my_curl.toml`:

```toml
tags = ["http"]
developer = ["you"]
ports_separator = ","
call_format = "curl -s http://{{ip}}:{{port}}/"
```

Then run with the custom engine:

```bash
rustscan -a 192.0.2.10 -p 80,443,8080 --scripts custom
```

For each open port matching the script's `tags`, RustScan substitutes `{{ip}}` and `{{port}}` placeholders into `call_format` and executes it. Anything you pass after `--` is appended to the call too.

> [!NOTE]
> The community has reported that custom scripts on Windows are unreliable (issue #513). Stick with WSL2 or the Docker image if you need scripting on Windows.

### 5.7 Tune `--ulimit` and `--batch-size` for fast LANs

On a server with plenty of file descriptors and a quiet LAN you can crank both:

```bash
sudo rustscan -a 10.0.0.0/24 -u 10000 -b 9000 -t 800
```

The `-u` flag calls `setrlimit(RLIMIT_NOFILE)` automatically; on most systems this requires `sudo` or `CAP_SYS_RESOURCE`. The internal auto-tuner caps the effective batch at `ulimit - 100` so you stay under the OS limit.

> [!TIP]
> If you don't pass `-u`, RustScan logs the current limit and tells you exactly what `-b` you could safely use: `[~] File limit higher than batch size. Can increase speed by increasing batch size '-b 7900'.`

### 5.8 Run RustScan inside a CI job (Docker)

GitHub Actions, GitLab CI, or any pipeline that can run a container:

```yaml
- name: Discover open ports
  run: |
    docker run --rm rustscan/rustscan:latest \
      -g --no-banner -a ${{ secrets.SCAN_TARGET }} -r 1-1024 \
      | tee scan-results.txt
```

The image already has Nmap, so you can also append `-- -sV` if you want service banners in the same step.

> [!CAUTION]
> Treat scan output as sensitive — open ports on internal hosts plus a `-sV` fingerprint is exactly the recon data an attacker would want. Store CI artifacts in a private bucket and rotate them on a short retention window.

---

## 6. Troubleshooting

Diagnostic decision tree:

1. **No output at all?** — If `--greppable` and no banner are off, RustScan should always print at least the banner. Try removing `-g` to see warnings.
2. **`No IPs could be resolved`?** — The `-a` value didn't parse as IP/CIDR/hostname/file. See "could not be resolved" below.
3. **Found 0 ports but you know there are some open?** — Almost always batch size or timeout. See "Looks like I didn't find any open ports".
4. **Nmap crashed afterwards?** — That's separate. Try `--scripts none` to confirm RustScan itself is healthy.

---

### Error: `[!] No IPs could be resolved, aborting scan.`

**Cause:** Every value in `-a` failed to parse as an IP, CIDR, hostname, or path to a file containing those.

**Solution:**

1. Quote any address with shell metacharacters.
2. If it's a hostname, check DNS: `dig +short example.com`.
3. If it's a file, confirm the path: `ls -l hosts.txt` and that each line is one address.
4. Try an explicit resolver: `--resolver 1.1.1.1,8.8.8.8`.

**Prevention:** When using `-a hosts.txt`, validate the file first: `awk 'NF' hosts.txt | head` to confirm content.

---

### Error: `Host "example.com" could not be resolved.`

**Cause:** `parse_address` couldn't resolve the hostname through the system resolver, the Cloudflare fallback, or any custom resolver you supplied.

**Solution:**

1. Confirm DNS works at all: `dig example.com`.
2. If you're behind a corporate DNS, pass it explicitly: `--resolver 10.0.0.53`.
3. Use a comma-separated list for redundancy: `--resolver 10.0.0.53,1.1.1.1`.
4. Or pass a file of resolver IPs: `--resolver /etc/rustscan-resolvers.txt`.

**Prevention:** Stick to IPs/CIDRs in scripted scans — no DNS, no DNS problems.

---

### Error: `Looks like I didn't find any open ports for "10.0.0.1". This is usually caused by a high batch size.`

**Cause:** RustScan completed the scan but every port returned a connection error. The most common reason is a batch size larger than the host or the network can absorb (SYN flood backpressure, IDS blocking, kernel running out of conntrack entries).

**Solution:**

1. Halve the batch size: `-b 2500`.
2. Increase the timeout: `-t 3000`.
3. Increase retries: `--tries 2`.
4. Run with `RUST_LOG=debug` to see the per-socket error strings.

**Prevention:** Start gentle (`-b 1500 -t 2000`) on unfamiliar networks and ramp up.

---

### Error: `[!] ERROR. Failed to set ulimit value.`

**Cause:** RustScan tried to call `setrlimit(RLIMIT_NOFILE)` but lacked permission. Typical on macOS where the per-process hard limit is enforced, and on Linux without `CAP_SYS_RESOURCE`.

**Solution:**

1. Run the command with `sudo`.
2. Raise the system-wide hard limit (`/etc/security/limits.conf` on Linux, `launchctl limit maxfiles` on macOS) and re-login.
3. Or skip `-u` and let the auto-tuner pick a safe batch size.

**Prevention:** On long-lived hosts, persist `nofile` ulimits via systemd / launchd so you never hit this interactively.

---

### Error: Nmap exits with `Error Exit code = 1` (issues #864 / #862)

**Cause:** Either Nmap isn't on `$PATH` (`Error Exit code = 127`) or a flag passed after `--` is invalid for your Nmap version. Both have been reported by the community.

**Solution:**

1. Confirm Nmap is installed: `nmap --version`.
2. Re-run with `--scripts none` to confirm RustScan itself is healthy.
3. Remove any custom flags after `--` and add them back one at a time.
4. As a workaround, pipe greppable output into a Nmap call you control: `rustscan -g ... | awk ...`.

**Prevention:** Stick to the Docker image when you need a known-good Nmap pairing.

---

### Error: `Could not infer config file path.`

**Cause:** The `dirs` crate couldn't determine `XDG_CONFIG_HOME` or `$HOME`. Almost only happens in extremely minimal containers.

**Solution:**

1. Set `HOME` and/or `XDG_CONFIG_HOME` in your environment.
2. Or pass `--config-path /path/to/your.toml` explicitly.
3. Or pass `--no-config` to skip the file entirely.

**Prevention:** Keep `HOME` set in your container images.

---

### Error: `cargo install` fails with `env_logger` mismatch (issue #824)

**Cause:** Older Rust toolchains can't resolve the dependency tree at the pinned `env_logger 0.11.x`. Reported on Debian 12.

**Solution:**

1. Update Rust: `rustup update stable`.
2. Or use the Docker image / prebuilt `.deb` instead.

**Prevention:** Keep `rustup` current; pin Rust >= 1.75 in CI.

---

### Error: `Found <toml> in configuration file. Aborting scan.`

**Cause:** TOML parse error in your config file. RustScan refuses to guess at intent.

**Solution:**

1. Run `toml-cli get ~/.config/.rustscan.toml` (or any TOML linter) to find the bad line.
2. Or temporarily run with `--no-config` to confirm everything else works.

**Prevention:** Validate config files in version control with `taplo lint`.

---

### Error: `Too many open files. Please reduce batch size.` (assertion panic)

**Cause:** A socket connect returned `ErrorKind::Other` containing "too many open files" — this is a hard panic by design because the scan results would be silently incomplete.

**Solution:**

1. Lower batch size: `-b 2500` or even `-b 1000`.
2. Raise ulimit explicitly: `-u 8000`.
3. Combine: `-u 8000 -b 7900`.

**Prevention:** Don't run scans on systems with `nofile = 1024`; raise it once at the OS level.

---

## 7. FAQ

**Q: Why does RustScan default to scanning all 65,535 ports?**

Because that's the fastest way to find weird things. RustScan's whole point is "the modern day port scanner": let the speed solve the problem instead of you guessing which 1000 ports matter. If you do want a top-1000 sweep, use `--top` (with a `ports = [...]` list in your config) or `-p 1-1000`.

**Q: How do I make RustScan run without Nmap at all?**

Pass `--scripts none`. You'll only get the open-port list, no Nmap handoff. If you also want machine-friendly output, combine with `-g`.

**Q: What's the difference between `--scan-order serial` and `random`?**

Serial walks ports `1, 2, 3, …` in ascending order. Random uses a Linear Congruential Generator: it picks a random starting port and a coprime step, then jumps through the range. The output is fully unique (no port visited twice) but not predictable, which can fly under naive IDS rules.

**Q: Can I pipe addresses in via stdin?**

Not directly — `-a` doesn't read stdin. Put your IPs in a file and use `-a hosts.txt`, or use shell substitution: `rustscan -a "$(paste -sd, hosts.txt)"`.

**Q: Where exactly does RustScan look for the config file?**

In order: the path you passed via `-c PATH`; then `$XDG_CONFIG_HOME/.rustscan.toml` (typically `~/.config/.rustscan.toml`); then `~/.rustscan.toml` for backwards compatibility. If none exist, defaults are used.

**Q: How do I disable colored output?**

Pass `--accessible`. That also strips the ASCII art, which makes screen readers happy. Greppable mode (`-g`) implicitly drops color too.

**Q: My scan finds way fewer ports than `nmap -sS`. Why?**

RustScan does TCP connect (`connect()`) scans, not raw SYN scans. Stateful firewalls drop incomplete handshakes the same on both, but a connect scan completes the full TCP handshake which some IPS solutions flag and reset. If you're allowed root and need stealth, do RustScan for fast discovery and Nmap `-sS` for the deeper sweep: `rustscan -g -a TARGET | xargs -I{} nmap -sS -p {}`.

**Q: Does RustScan support proxies?**

Not at the moment. Proxy support is tracked as a feature request (#825). Use `proxychains` or run RustScan inside a network namespace that already routes through your proxy.

**Q: Does RustScan support combined port-range syntax like `1-22,8080,8443`?**

Not in 2.4.1 — `-p` accepts a comma list and `-r` accepts a single `start-end` range, and they're mutually exclusive. Combined syntax is tracked in #780 / #835.

**Q: How do I distinguish TCP from UDP results?**

Run two scans, one without `--udp` and one with. UDP scanning uses the embedded `nmap-payloads` database to send service-specific probes and listens for any reply. Mixed `host/tcp + host/udp` syntax is a feature request (#829).

**Q: Why does my Docker scan find different ports than my native scan?**

The Docker image runs as root with high default ulimits, so its batch size is whatever you ask for. A native scan as a regular user falls back to whatever your shell's `nofile` is. Compare with `ulimit -n` and pass `-u` accordingly.

**Q: Can I get the first open port in a range and stop?**

Not in the current release; tracked as #822. RustScan always exhausts the range. For a poor-man's first-port lookup, pipe `-g` output into `awk 'NR==1 {print $0; exit}'`.

**Q: How do I reset my config to defaults?**

Delete (or rename) `~/.config/.rustscan.toml` and `~/.rustscan.toml`, or run any scan with `--no-config`. RustScan's compiled-in defaults take over immediately.

**Q: Where do I report bugs and feature requests?**

[github.com/RustScan/RustScan/issues](https://github.com/RustScan/RustScan/issues). The community is active and responds quickly.

---

_Generated April 2026 from bee-san/RustScan at b033798. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
