Skip to content

Installation

One command

curl -fsSL https://3270connect.3270.io/install.sh | bash

The installer asks how you want it and gets out of the way. Four doors:

Binary a single file on your PATH s3270 is bundled — nothing else to install
Docker one container, the operations console ghcr.io/3270io/3270connect
Compose a stack you can edit and re-up writes ./3270connect/docker-compose.yml
Lab the console, a browser terminal and a 3270 host to aim at three containers, no mainframe required

Skip the questions by naming the method:

curl -fsSL https://3270connect.3270.io/install.sh | bash -s -- --method lab --yes
Option Default Purpose
--method ask binary, docker, compose or lab
--version latest A release tag, e.g. v2.0
--port 9200 Host port for the console
--bind 127.0.0.1 Host interface to publish on
--dir ./3270connect Compose project directory (./3270-lab for the lab)
--system / --user user Binary install to /usr/local/bin or ~/.local/bin
--dry-run off Print what would happen, change nothing
--yes off Accept every prompt

Re-running the installer updates an install that is already here rather than replacing it: the port it is published on, the version it is pinned to and the data folder holding its run history are all carried forward unless you pass something different. It finds that install through Docker, not through the directory you happen to be standing in, so running the one-line command a second time from somewhere else does not strand the first one.

The console has no sign-in until you turn one on

Out of the box its start-process dialog launches a load run — against any host it is given — for whoever can open the page. That is why every method publishes on 127.0.0.1 by default.

Two ways to share it. Forward the port over SSH, which needs no configuration:

ssh -L 9200:localhost:9200 user@runner-01

Or give it accounts, which is what to do when more than one person needs it:

AUTH_MODE=local

That adds a sign-in page, per-user passwords, an administration area and an audit trail. See Accounts and Sign-In. Either way, put TLS in front before publishing it on a network you do not control.


The lab

The question after installing an automation tool is always "against what?". The lab answers it: a TN3270 host, a browser terminal to explore it by hand, and the console to replay what you did there.

curl -fsSL https://3270connect.3270.io/install.sh | bash -s -- --method lab
http://localhost:3270    the terminal. Connect to host "sampleapps", port 3271
http://localhost:9200    the console

The applications are 3270Web's bundled samples — a pet store with a back office on 3271, and the name-entry example on 3272 — served as an ordinary TN3270 host, so they negotiate, hold session state and behave like the real thing. Nothing there is a mainframe and nothing has data behind it, which is what makes it safe to point a load test at.

The stack ships with a workflow already aimed at it:

cd 3270-lab
docker compose run --rm 3270connect -config workflow-sampleapp.json -headless

The same three services are checked into both repositories as docker-compose.lab.yml, if you would rather start from a file you can read first.


Docker

The image runs the operations console by default and takes any other set of flags as its command, so one image covers both.

docker run -d --name 3270connect \
  -p 127.0.0.1:9200:9200 \
  -e DASHBOARD_BIND=0.0.0.0 \
  -v "$PWD/data":/data \
  ghcr.io/3270io/3270connect:latest

Then open http://localhost:9200/dashboard.

docker run --rm \
  -v "$PWD/workflow.json":/data/workflow.json \
  -v "$PWD/data":/data \
  ghcr.io/3270io/3270connect:latest -config workflow.json -headless
docker run --rm \
  -v "$PWD/data":/data \
  ghcr.io/3270io/3270connect:latest \
  -config workflow.json -headless -concurrent 25 -runtime 600
docker run --rm -p 3270:3270 \
  ghcr.io/3270io/3270connect:latest -runApp 1 -runApp-port 3270

DASHBOARD_BIND and why the image sets it

The console binds localhost, which is right on a laptop and useless in a container: a published port forwards to the container's external interface, so a loopback listener refuses every connection from the host while the container still reports healthy. The image sets DASHBOARD_BIND=0.0.0.0 for that reason. What the console is exposed to is decided by the port mapping, not by that variable.

/data and who owns it

The container writes the metrics each run publishes, the console's logs and any workflow output to /data, and runs unprivileged as uid 10001. A bind-mounted host folder keeps the host's ownership, so hand it over before the first start:

mkdir -p ./data && sudo chown -R 10001:10001 ./data

A named volume needs none of this — Docker sets its ownership from the image.

Compose

docker-compose.yml in the repository runs the console on its own:

docker compose up -d
docker compose run --rm 3270connect -config workflow.json -headless

The installer writes an equivalent stack, pinned to a version and configured with the port and data folder you chose.

linux/amd64 only

The s3270 the emulator drives is embedded in the binary as an x86-64 executable and written out when a workflow first connects, so there is no arm64 image: it would start, serve the console, and fail the first run. On Apple silicon the image runs under emulation.


Linux

Direct download

# The latest release, straight from GitHub
curl -fsSLO https://github.com/3270io/3270Connect/releases/latest/download/3270Connect_linux

chmod +x 3270Connect_linux
sudo mv 3270Connect_linux /usr/local/bin/3270connect

The binary is self-contained: s3270 travels inside it and is written to the temporary directory the first time a workflow connects. Run history goes to ~/.config/3270Connect/, which is where the console reads it from.

3270connect -dashboard                       # the console on :9200
3270connect -config workflow.json            # one run
3270connect -runApp 1 -runApp-port 3270      # a 3270 host to aim at

Windows

Direct download

$latest = "https://github.com/3270io/3270Connect/releases/latest/download/3270Connect.exe"
Invoke-WebRequest -Uri $latest -OutFile 3270Connect.exe

# Optionally, move it somewhere on PATH (requires admin)
# Move-Item -Path 3270Connect.exe -Destination "C:\Program Files\3270Connect.exe"

Double-clicking 3270Connect.exe with no arguments starts the console and opens it in a window.

Docker

A Windows container image is built from Dockerfile.windows and is not published: a Windows image cannot be built on a Linux runner. Build it yourself where you need it:

docker build -f Dockerfile.windows -t 3270connect-windows .

Building from source

git clone https://github.com/3270io/3270Connect.git
cd 3270Connect
./build.sh          # binaries into dist/
go test ./...

Go 1.25 or newer. The x3270 binaries embedded in the build are produced separately; see BUILD.md.