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 the relay command action, parses flags including --host, --ports, and --pass, and orchestrates tcp.Run calls (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: Contains DEFAULT_RELAY and DEFAULT_RELAY6 constants (lines 18-22) that clients use when no custom relay is specified.

Summary

  • The croc relay in src/cli/cli.go creates a stand-alone TCP server using tcp.Run from src/tcp/tcp.go to 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,9013 and point clients to it via --relay or CROC_RELAY.
  • Secure your infrastructure using the --pass flag, processed by determinePass in src/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →