How to Self-Host a croc Relay Server: Complete Setup Guide
Running croc relay starts a stand-alone TCP server that forwards byte streams between clients, letting you host your own secure file-transfer infrastructure independent of the public relay.
The croc project by schollz/croc enables secure file transfers through a command-line interface. While it defaults to public relay servers, you can self-host a croc relay server to maintain complete control over your data routing and eliminate dependency on third-party infrastructure.
What the croc Relay Does
The croc relay acts as a TCP-to-WebSocket bridge that forwards an opaque byte stream between a sender and receiver. When you execute the relay command, the CLI invokes the relay action defined in src/cli/cli.go (lines 25-33), which parses flags and initiates listeners. The core TCP server logic resides in src/tcp/tcp.go (lines 1-80), where the tcp.Run function spawns listeners for each configured port.
Relay Configuration Options
The relay behavior is controlled through several flags parsed in src/cli/cli.go:
| Flag | Default | Description |
|---|---|---|
--host |
empty (binds all) | Hostname or IP address |
--ports |
9009,9010,9011,9012,9013 |
Comma-separated port list |
--port |
9009 |
Base port when --ports is omitted |
--transfers |
5 |
Number of data ports (control port added automatically) |
--debug |
false |
Enable verbose logging |
The first port in the list serves as the control channel, while remaining ports handle actual file-transfer streams.
Step-by-Step Self-Hosting Guide
1. Prepare Your Server and Firewall
Select a VPS or dedicated server with a public IP. Open the chosen ports in your firewall—the first port handles control signals, while the others manage data transfers. For the default configuration, allow inbound TCP on ports 9009 through 9013.
2. Start the Relay Process
Execute the relay binary with your desired host and port configuration:
croc relay --host 0.0.0.0 --ports 9009,9010,9011,9012,9013
This command triggers the relay(c *cli.Context) function in src/cli/cli.go (lines 34-49), which builds the port slice. It then calls tcp.Run(debugString, host, portStr, determinePass(c)) for each port (lines 60-66), creating TCP listeners. The first listener (port 9009) manages control connections, while the others await data streams.
3. Configure Clients to Use Your Relay
Direct croc clients to your self-hosted relay using the --relay flag or the CROC_RELAY environment variable:
# Sender command
croc send --relay my.relay.example.com:9009 myfile.txt
# Receiver command
croc --relay my.relay.example.com:9009
The client automatically negotiates one of the available data ports advertised through the control channel.
4. Implement Password Protection
Add authentication to prevent unauthorized use via the --pass flag or CROC_PASS environment variable. The determinePass function in src/cli/cli.go (lines 79-87) processes these values and passes them to tcp.Run, requiring clients to present the same password to connect.
croc relay --host 0.0.0.0 --ports 9009,9010,9011,9012,9013 --pass your-secret-password
5. Deploy as a Persistent Service
Systemd Service: Create a systemd unit file to run the relay continuously:
[Unit]
Description=croc Relay
After=network.target
[Service]
ExecStart=/usr/local/bin/croc relay --host 0.0.0.0 --ports 9009,9010,9011,9012,9013
Restart=on-failure
User=nobody
Group=nobody
[Install]
WantedBy=multi-user.target
Docker Container: Build a lightweight container using this Dockerfile:
FROM golang:1.22-alpine AS builder
WORKDIR /src
RUN apk add --no-cache git
RUN git clone https://github.com/schollz/croc.git .
RUN go build -mod=readonly -o /croc ./main.go
FROM alpine:latest
COPY --from=builder /croc /usr/local/bin/croc
EXPOSE 9009 9010 9011 9012 9013
ENTRYPOINT ["croc","relay","--host","0.0.0.0","--ports","9009,9010,9011,9012,9013"]
Key Implementation Files
Understanding the source structure helps with customization:
src/cli/cli.go: Defines therelaycommand action, parses flags including--host,--ports, and--pass, and orchestratestcp.Runcalls (lines 25-33, 60-66).src/tcp/tcp.go: Implements the TCP server logic that accepts connections and forwards the opaque byte stream (lines 1-80).src/webrelay/webrelay.go: Optional web interface and WebSocket bridge for browser-based transfers (lines 59-66), not required for basic relay functionality.src/models/constants.go: ContainsDEFAULT_RELAYandDEFAULT_RELAY6constants (lines 18-22) that clients use when no custom relay is specified.
Summary
- The croc relay in
src/cli/cli.gocreates a stand-alone TCP server usingtcp.Runfromsrc/tcp/tcp.goto bridge file transfers. - The first port specified serves as the control channel; subsequent ports handle data streams.
- Deploy the relay with
croc relay --host 0.0.0.0 --ports 9009,9010,9011,9012,9013and point clients to it via--relayorCROC_RELAY. - Secure your infrastructure using the
--passflag, processed bydeterminePassinsrc/cli/cli.go. - Run the binary as a systemd service or Docker container for production deployments.
Frequently Asked Questions
Do I need the web relay component to run a basic croc relay?
No. The src/webrelay/webrelay.go file provides an optional web UI and WebSocket bridge for browser-based transfers, but the core relay functionality in src/tcp/tcp.go operates independently as a pure TCP server.
How many ports should I open for the croc relay?
You need one control port plus one port per concurrent transfer. The --transfers flag defaults to 5, meaning you should open 6 ports total (control plus 5 data ports). The first port in your --ports list always serves as the control channel.
Can I use environment variables instead of command-line flags?
Yes. You can set CROC_RELAY on client machines to specify the relay address, and CROC_PASS to set the relay password. The determinePass function in src/cli/cli.go (lines 79-87) checks for the CROC_PASS environment variable when the --pass flag is not provided.
What is the difference between the control port and data ports?
The first port in your --ports list functions as the control channel where clients negotiate connections and receive the list of available data ports. The remaining ports handle the actual file-transfer byte streams. This separation allows the relay to manage multiple concurrent transfers without blocking the control signal path.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →