cheat sheet
z/OS FTP Reference
FTP from TSO, batch FTP via JCL, SITE parameters, transfer modes, FTPS, FTP.DATA configuration, and JES spool transfers.
z/OS FTP Reference
What it is
z/OS FTP is IBM's built-in file transfer client, available in every z/OS installation as part of the IBM Communications Server TCP/IP stack. It extends standard FTP with mainframe-specific SITE commands for specifying dataset attributes — record format (RECFM), logical record length (LRECL), blocksize, and space allocation — that the remote system needs to create properly structured datasets. Reach for z/OS FTP when transferring sequential datasets, PDS members, or JES spool output between z/OS and other systems; for scripted automation, embed FTP commands in JCL or REXX procedures.
Starting FTP from TSO
FTP hostname
FTP hostname(port)
FTP hostname PORT(2121)
FTP hostname SECURE (* explicit TLS/FTPS *)
FTP hostname VERBOSE (* show all server responses *)
After connecting you are prompted for userid and password. To skip the prompt, use a NETRC file (see below).
Interactive FTP subcommands
USER userid password (* re-authenticate or initial login *)
CD remotedir (* change remote directory *)
LCD localdsn (* change local dataset HLQ prefix *)
PWD (* print remote working directory *)
DIR [pattern] (* detailed directory/member listing *)
LS [pattern] (* brief listing *)
GET remote local (* download remote file to local dataset *)
PUT local remote (* upload local dataset to remote file *)
MGET pattern (* download multiple files matching pattern *)
MPUT pattern (* upload multiple datasets matching pattern *)
APPEND local remote (* append local data to remote file *)
DELETE remotefile (* delete remote file *)
RENAME old new (* rename remote file *)
MKDIR remotedir (* create remote directory *)
RMDIR remotedir (* remove remote directory *)
QUOTE cmd (* send raw FTP command to server *)
SITE params (* send SITE parameters to remote FTP server *)
PASSIVE (* toggle passive vs active mode *)
STATUS (* show current connection and settings *)
TYPE A (* ASCII transfer mode — EBCDIC↔ASCII translation *)
TYPE B (* BINARY / IMAGE — no translation *)
TYPE E (* EBCDIC — mainframe-to-mainframe, no translation *)
SENDSITE ON|OFF (* auto-send SITE before each transfer *)
SENDPASV ON|OFF (* passive mode toggle *)
LOCSITE params (* set allocation defaults for local receives *)
LRECL nnn (* set local LRECL for incoming transfers *)
BLKSIZE nnn (* set local BLKSIZE for incoming transfers *)
RECFM type (* set local RECFM for incoming transfers *)
BLOCK nnn (* set block count for transfers *)
OPEN hostname (* connect to a new host without restarting FTP *)
CLOSE (* disconnect from current host, stay in FTP *)
QUIT (* disconnect and exit FTP *)
Transfer types
| Type | Command | Use when |
|---|---|---|
| ASCII | TYPE A | Text files between mainframe and non-mainframe; translates EBCDIC↔ASCII |
| Binary/Image | TYPE B or TYPE I | Load modules, compiled objects, binary data; no translation |
| EBCDIC | TYPE E | Mainframe-to-mainframe text transfers; no translation |
TYPE A (* ASCII — default for most z/OS FTP servers *)
TYPE B (* Binary/Image *)
TYPE E (* EBCDIC *)
Tip: Always set
TYPE Bbefore transferring load libraries, VSAM exports, or any non-text data. Forgetting this is the most common cause of corrupted transfers.
SITE subcommand — z/OS allocation parameters
SITE passes dataset allocation instructions to the remote z/OS FTP server before PUT/MPUT. These parameters control how the destination dataset is created.
Record format and space:
SITE RECFM=FB LRECL=80 BLKSIZE=27920 SPACE=(TRK,(5,5)) UNIT=SYSDA
SITE RECFM=VB LRECL=255 BLKSIZE=32760 SPACE=(CYL,(1,1))
SITE RECFM=U BLKSIZE=32760 (* load modules *)
SITE RECFM=FB LRECL=133 (* print/report lines *)
Dataset organization:
SITE PDS (* allocate as PDS *)
SITE PDSE (* allocate as PDSE *)
SITE DSNTYPE=LIBRARY (* PDSE via DSNTYPE keyword *)
SITE FILETYPE=SEQ (* sequential dataset — default *)
SITE FILETYPE=JES (* submit to JES / retrieve spool *)
SMS classes:
SITE MGMTCLAS=STANDARD STORCLAS=FAST DATACLAS=FB80
SITE DATACLAS=VB255
SITE STORCLAS=PREMIUM
Space units:
SITE CYLINDERS SPACE=(5,2)
SITE TRACKS SPACE=(100,20)
SITE BLOCKS SPACE=(27920,(100,20))
SITE BLOCKSIZE=0 (* let system determine optimal BLKSIZE *)
Translation and encoding:
SITE XLATE ON (* enable translation table *)
SITE XLATE OFF
SITE ENCODING=IBM-1047 (* specific EBCDIC code page *)
SITE ENCODING=ISO8859-1 (* ASCII code page *)
Multiple parameters on one line:
SITE RECFM=FB LRECL=80 BLKSIZE=27920 SPACE=(TRK,(10,5)) UNIT=SYSDA
SITE RECFM=VB LRECL=255 BLKSIZE=32760 SPACE=(CYL,(2,1)) MGMTCLAS=STANDARD
Dataset naming — quoted vs unquoted
Quoting dataset names with single quotes forces absolute naming (no prefix appended):
GET 'SYS1.PROCLIB(IEFBR14)' iefbr14.jcl (* explicit absolute name *)
PUT localfile 'MY.TARGET.DATASET' (* absolute — no userid prefix *)
Without quotes the FTP server prepends your TSO prefix (usually userid) as the high-level qualifier:
GET PROCLIB(IEFBR14) (* becomes userid.PROCLIB(IEFBR14) *)
PUT myfile DATA (* becomes userid.DATA *)
PDS member transfers
Upload to a specific member:
PUT localfile 'MY.PDS.LIB(MEMBER)'
Upload multiple files — member names taken from filename stems:
MPUT *.cbl 'MY.COBOL.SRC(*)'
MPUT *.jcl 'MY.JCL.LIB(*)'
Download all members of a PDS:
CD 'MY.SOURCE.LIB'
DIR (* verify members *)
MGET * (* download all members as separate local files *)
Download one member:
GET 'MY.PDS.LIB(MEMBER)' member.txt
Submitting JCL and retrieving spool via FTP (FILETYPE=JES)
Switch to JES mode, submit, and retrieve output:
SITE FILETYPE=JES
PUT myjob.jcl (* submit — FTP returns JOBID e.g. JOB00123 *)
DIR (* list your jobs and their status *)
DIR JOB00123 (* list sysout datasets for specific job *)
GET JOB00123.2 output.txt (* retrieve sysout dataset 2 (JESMSGLG, JESJCL, etc.) *)
DELETE JOB00123 (* purge all sysout for this job *)
SITE FILETYPE=SEQ (* switch back to normal sequential mode *)
JES dataset numbers:
| Sysout DD | Meaning |
|---|---|
.1 | JESMSGLG — JES message log |
.2 | JESJCL — expanded JCL |
.3 | JESYSMSG — JES/system messages |
.4+ | Application SYSOUT DD statements in step order |
Batch FTP via JCL
Standard batch download:
//FTPGET JOB (ACCT),'FTP GET',CLASS=A,MSGCLASS=X
//FTP EXEC PGM=FTP,PARM='remotehost'
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//INPUT DD *
ftpuser
ftppass
TYPE B
GET remotefile 'MY.RECEIVED.DATA'
QUIT
/*
Batch upload with SITE parameters:
//FTPPUT JOB (ACCT),'FTP PUT',CLASS=A,MSGCLASS=X
//FTP EXEC PGM=FTP,PARM='remotehost'
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//INPUT DD *
ftpuser
ftppass
SITE RECFM=FB LRECL=80 BLKSIZE=27920 SPACE=(TRK,(5,5))
PUT 'MY.JCL.LIB(MYJOB)' MYJOB.JCL
QUIT
/*
Batch submit JCL to remote system via JES mode:
//FTPSUBMT JOB (ACCT),'REMOTE SUBMIT',CLASS=A,MSGCLASS=X
//FTP EXEC PGM=FTP,PARM='remotezos'
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//INPUT DD *
ftpuser
ftppass
SITE FILETYPE=JES
PUT 'MY.JCL.LIB(BATCHJOB)'
QUIT
/*
Batch FTP with NETRC auto-login:
//FTPNETRC JOB (ACCT),'FTP NETRC',CLASS=A,MSGCLASS=X
//FTP EXEC PGM=FTP,PARM='remotehost'
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//NETRC DD DSN=MY.NETRC,DISP=SHR
//INPUT DD *
TYPE B
GET remotefile 'MY.RECEIVED.DATA'
QUIT
/*
Multiple transfers in one batch job:
//FTPMULTI JOB (ACCT),'MULTI FTP',CLASS=A,MSGCLASS=X
//FTP EXEC PGM=FTP,PARM='remotehost'
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//INPUT DD *
ftpuser
ftppass
TYPE B
SITE RECFM=FB LRECL=80 BLKSIZE=27920 SPACE=(TRK,(5,5))
PUT 'MY.FIRST.DATA' first.dat
PUT 'MY.SECOND.DATA' second.dat
TYPE A
PUT 'MY.REPORT.DATA' report.txt
QUIT
/*
Specifying a non-default port:
//FTP EXEC PGM=FTP,PARM='remotehost 2121'
FTP.DATA — client configuration
FTP.DATA (or hlq.FTP.DATA) controls default FTP client settings. Search order at session start:
userid.FTP.DATA(personal)FTP.DATAunder current prefixSYS1.TCPPARMS(FTPDATA)(site-wide default)
Common FTP.DATA keywords:
FTPKEEPALIVE 60 (* send keepalive every 60 seconds *)
DATATYPE ASCII (* default transfer type: ASCII, BINARY, or EBCDIC *)
SENDSITE TRUE (* auto-send SITE params before each transfer *)
PASSIVE FALSE (* use active mode; TRUE for passive *)
SENDPASV FALSE (* do not send PASV by default *)
VERBOSE FALSE (* suppress server response display *)
NODNS TRUE (* use IP addresses only, skip DNS *)
PROXY proxyhost(port) (* FTP proxy *)
AUTOLOCSITE TRUE (* mirror remote SITE params to local *)
SBDATACONN TRUE (* secondary data connection binding *)
LOCSITE RECFM=FB LRECL=80 BLKSIZE=27920 (* default local allocation for GET *)
LOCSITE SPACE=(TRK,(5,5)) UNIT=SYSDA
TLSMECHANISM TLS (* force TLS — see FTPS section *)
SECURE_CTRLCONN PRIVATE (* require encrypted control connection *)
SECURE_DATACONN PRIVATE (* require encrypted data connection *)
NETRC — automatic login
NETRC (or userid.NETRC) stores credentials for password-free FTP sessions:
MACHINE remotehost LOGIN ftpuser PASSWORD ftppass
MACHINE prod.zos.example.com LOGIN produser PASSWORD s3cr3t
MACHINE default LOGIN anonymous PASSWORD user@example.com
Reference in JCL with //NETRC DD DSN=MY.NETRC,DISP=SHR. Protect this dataset with RACF — it contains cleartext passwords.
FTPS — FTP over TLS (explicit)
z/OS FTP supports explicit TLS (FTPS). This is not SFTP — it uses standard FTP protocol over a TLS channel.
From TSO:
FTP hostname SECURE
FTP hostname SECURE PORT(990) (* implicit TLS on port 990 *)
In FTP.DATA for always-on TLS:
TLSMECHANISM TLS
SECURE_CTRLCONN PRIVATE
SECURE_DATACONN PRIVATE
In batch JCL:
//FTP EXEC PGM=FTP,PARM='hostname SECURE'
AT-TLS (Application Transparent TLS) can also be configured at the TCP/IP policy level to enforce TLS transparently without application changes — contact your network/security team.
LOCSITE — default allocation for incoming transfers
LOCSITE sets allocation defaults for datasets created during GET/MGET on the local (z/OS) side. Can be set in FTP.DATA or issued interactively:
LOCSITE RECFM=FB LRECL=80 BLKSIZE=27920 SPACE=(TRK,(5,5))
LOCSITE RECFM=VB LRECL=255 BLKSIZE=32760 SPACE=(CYL,(1,1))
LOCSITE PDSE (* incoming PDS transfers become PDSE *)
LOCSITE UNIT=SYSDA STORCLAS=STANDARD
Common patterns
Download a single member:
GET 'PROD.PROCLIB(STARTED)' started.proc
Upload and replace a member:
SITE RECFM=FB LRECL=80
PUT deploy.jcl 'MY.JCL.LIB(DEPLOY)'
Transfer between two z/OS systems (EBCDIC, no translation):
TYPE E
GET 'REMOTE.DS' 'LOCAL.DS'
Bulk download from remote directory:
CD /u/myuser/exports
MGET *.csv
Retrieve multiple spool outputs from a batch job:
SITE FILETYPE=JES
DIR JOB00456
GET JOB00456.3 sysmsgs.txt
GET JOB00456.4 sysprint.txt
DELETE JOB00456
SITE FILETYPE=SEQ
Passive mode for firewall-friendly transfers:
PASSIVE
GET remotefile localfile
FTP return codes
| Range | Meaning |
|---|---|
| 1xx | Positive preliminary — action initiated |
| 2xx | Positive completion — action succeeded |
| 3xx | Positive intermediate — more information needed |
| 4xx | Transient negative — temporary failure, retry may work |
| 5xx | Permanent negative — do not retry without fixing the cause |
Common codes:
| Code | Meaning |
|---|---|
| 125 | Data connection open, transfer starting |
| 150 | File status OK, opening data connection |
| 200 | Command OK |
| 220 | Service ready (server greeting) |
| 226 | Transfer complete, closing data connection |
| 230 | User logged in |
| 250 | Requested action OK |
| 257 | Pathname created |
| 331 | Username OK, password required |
| 425 | Can't open data connection |
| 426 | Connection closed, transfer aborted |
| 500 | Syntax error / command unrecognized |
| 501 | Syntax error in parameters |
| 530 | Not logged in / authentication failed |
| 550 | File unavailable — not found, no RACF access, or wrong filetype |
| 553 | File name not allowed |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
425 Can't open data connection | Firewall blocking data port | Issue PASSIVE command or open FTP data port range |
| Garbled text after transfer | Wrong transfer type | Use TYPE A for text, TYPE B for binary |
| Dataset not found (550) | Missing quotes on absolute name | Add single quotes around fully qualified names |
| Userid prefix appended unexpectedly | Unquoted dataset name | Quote the dataset name: 'SYS1.PROCLIB' |
| ABEND in batch FTP | Server-side or allocation error | Check SYSPRINT for response codes and messages |
| Permission denied (550) | RACF access denied | Verify dataset profile and userid permissions |
| TLS handshake failure | Certificate not trusted | Check AT-TLS policy; may need to add server cert to keyring |
| Transfer complete but data wrong | Binary file sent as ASCII | Re-transfer with TYPE B |
| Member truncated | Wrong LRECL | Use SITE LRECL=nnn matching source LRECL |
| Batch job gets RC 12 | FTP authentication or allocation failure | Check SYSPRINT OUTPUT for 4xx/5xx response codes |
SITE keywords — full reference
The SITE subcommand passes z/OS-specific allocation instructions to the remote FTP server before each PUT. Most keywords mirror their JCL DD counterparts but use FTP-flavoured names. The current SITE settings are inherited by every subsequent transfer until changed; SITE RESET (or reconnecting) reverts to FTP.DATA defaults.
Record format and DCB
| Keyword | Equivalent JCL | Notes |
|---|---|---|
RECFM=FB / VB / U / FBA / VBA | DCB=(RECFM=…) | Fixed/variable, blocked, ANSI carriage control, undefined |
LRECL=nnn | LRECL=nnn | Logical record length; for VB include the 4-byte RDW |
BLKSIZE=nnn | BLKSIZE=nnn | BLKSIZE=0 lets SMS pick a system-determined optimum |
BLOCKSIZE=nnn | BLKSIZE=nnn | Synonym accepted by some FTP server levels |
BLOCKS / CYLINDERS / TRACKS | SPACE=(unit,…) | Allocation unit |
PRI=nnn | SPACE=(unit,(nnn,sec)) | Primary extent |
SEC=nnn | SPACE=(unit,(prim,nnn)) | Secondary extent |
DIRECTORY=nnn | SPACE=(unit,(p,s,nnn)) | PDS directory blocks |
PDS / PDSE / DSNTYPE=LIBRARY | DSNTYPE=LIBRARY | PDS vs PDSE |
LARGE | DSNTYPE=LARGE | Large-format sequential (>65535 tracks) |
EXTPREF / EXTREQ | DSNTYPE=EXTPREF/EXTREQ | Extended-format preferred / required |
RETPD=nnn | RETPD=nnn | Retention period in days |
EXPDT=yyyy/ddd | EXPDT=yyyy/ddd | Explicit expiration date |
STORCLAS=name | STORCLAS= | SMS storage class |
MGMTCLAS=name | MGMTCLAS= | SMS management class |
DATACLAS=name | DATACLAS= | SMS data class |
UNIT=type | UNIT= | Device class (SYSDA, TAPE, 3390) |
VOLUME=ser | VOL=SER= | Specific volume serial |
AVGREC= | AVGREC= | Allocation in records (U/K/M units) |
FILETYPE=SEQ / JES / SQL | n/a — FTP-only | Server side semantics: see below |
FILETYPE switches
FILETYPE is the single most important z/OS SITE keyword because it changes the entire semantic of GET/PUT.
| Value | Meaning |
|---|---|
SEQ | Default — transfers move bytes between local and remote sequential datasets / PDS members |
JES | PUT submits the local file as JCL; GET retrieves spool output; DIR lists your jobs |
SQL | The local file is a SQL statement passed to DB2; the result set is returned as the transfer |
SITE FILETYPE=SEQ (* normal file mode — default *)
SITE FILETYPE=JES (* submit JCL, retrieve spool *)
SITE FILETYPE=SQL (* DB2 query mode *)
Submitting JCL via FTP
The canonical workflow: switch to JES mode, PUT the JCL file, and FTP reads back the JOBID from the server response. DIR lists jobs you own; GET JOBnnnnn.n retrieves a specific spool dataset.
ftp> SITE FILETYPE=JES
200 SITE command was accepted
ftp> PUT alicejob.jcl
250-IT IS KNOWN TO JES AS JOB12345
250 Transfer completed successfully.
ftp> DIR
ALICE JOB12345 OUTPUT A RC=0000 3 spool files
ftp> GET JOB12345.4 stepout.txt
ftp> DELETE JOB12345 (* purge from spool *)
SITE JESJOBNAME=* (or a specific jobname) filters subsequent DIR listings. SITE JESOWNER= filters by owner. SITE JESSTATUS=ALL shows active and output queues; INPUT, ACTIVE, OUTPUT narrow the view.
Sample SITE combos by use case
(* 80-byte sequential source *)
SITE RECFM=FB LRECL=80 BLKSIZE=0 PRI=10 SEC=5 CYLINDERS UNIT=SYSDA
(* Load library *)
SITE RECFM=U BLKSIZE=32760 PRI=10 SEC=5 CYLINDERS DSNTYPE=LIBRARY DIRECTORY=20
(* PDSE source library *)
SITE RECFM=FB LRECL=80 BLKSIZE=0 PRI=10 SEC=5 CYLINDERS DSNTYPE=LIBRARY DIRECTORY=0
(* Variable-block report *)
SITE RECFM=VB LRECL=255 BLKSIZE=0 PRI=5 SEC=5 TRACKS
(* SMS-managed, let the class drive everything *)
SITE DATACLAS=DC80FB STORCLAS=SCSTD MGMTCLAS=MCRETN30
(* Large-format sequential >65k tracks *)
SITE RECFM=FB LRECL=80 BLKSIZE=0 PRI=200000 SEC=20000 TRACKS DSNTYPE=LARGE
Dataset name quoting — escape rules
Mainframe dataset names use . as a qualifier separator and FTP servers accept them unquoted only when the resulting name is a valid qualified name. Quoting rules differ between client libraries; for the z/OS FTP client these are the conventions you'll hit.
(* Absolute reference - single quotes *)
GET 'ALICE.SOURCE.LIB(MEMBER)' member.txt
(* Relative reference - the server prepends current working "directory" (HLQ) *)
LCD ALICE
CD SOURCE.LIB
GET MEMBER member.txt (* equivalent to ALICE.SOURCE.LIB(MEMBER) *)
(* PDS member - parenthesised as in JCL *)
PUT myjob.jcl 'ALICE.JCL.LIB(MYJOB)'
(* USS path - forward slashes; no quotes *)
GET /u/alice/data.csv data.csv
PUT data.csv /u/alice/upload.csv
(* Filenames with spaces or special chars on non-z/OS remotes - quote with double quotes *)
GET "Some File.txt" localfile.txt
(* Dataset with embedded special character - rare, but use double quotes inside single *)
GET '"ALICE.WEIRD-NAME.DATA"' weird.txt
A common confusion: the server's CD is interpreted as "change current HLQ prefix"; the client's LCD (local CD) changes the local prefix used for un-quoted local filenames. Both behave differently from filesystem CDs on Unix or Windows.
Stream vs block transfer mode
FTP supports several transmission modes; on z/OS only stream and block see real use. Stream mode (MODE S, default) sends a continuous byte stream and signals end-of-file by closing the data connection. Block mode (MODE B) wraps each record in a header that preserves record boundaries — required for transferring variable-length records intact to and from non-z/OS systems that wouldn't otherwise know where one record ends.
MODE S (* stream — default *)
MODE B (* block — preserves record structure *)
MODE C (* compressed — rarely supported *)
Use block mode for VB datasets sent to or from a Unix peer that supports it (most modern FTP servers do). Without block mode, VB records are flattened into a stream and the receiver has no way to reconstruct record boundaries unless it adds them by content inspection.
TYPE I (* binary — no translation *)
MODE B (* block — preserve records *)
PUT 'ALICE.VB.DATA' alice-vb.bin
EBCDIC ↔ ASCII translation
TYPE A (ASCII) enables EBCDIC↔ASCII translation; the default table is IBM-1047 (US English EBCDIC) ↔ ISO8859-1 (Latin-1). SITE ENCODING= overrides the EBCDIC side; the FTP server selects the ASCII side from the client's LANG setting or SITE CHARSET=.
TYPE A (* enable translation *)
SITE ENCODING=IBM-1047 (* US EBCDIC — default *)
SITE ENCODING=IBM-037 (* alternate US/Canada EBCDIC *)
SITE ENCODING=IBM-1140 (* Euro variant of 1047 *)
SITE ENCODING=IBM-273 (* German EBCDIC *)
SITE ENCODING=UTF-8 (* UTF-8 — newer FTP server levels *)
TYPE E (* EBCDIC mode — no translation, both ends are EBCDIC *)
Pitfalls: the [ ] { } ^ ~ characters are notoriously inconsistent between EBCDIC code pages, and a transfer that round-trips between codepage 1047 and 037 can silently corrupt those bytes. When in doubt, transfer as binary (TYPE I) and convert in a controlled step using iconv (z/OS USS) or the Java mainframe codec.
Wildcards — MGET and MPUT
MGET and MPUT accept shell-style wildcards; the matching is done on the source side (server for MGET, client for MPUT). * matches any characters; ? matches one character; bracket expressions are not supported by z/OS FTP.
MGET *.cbl (* download every .cbl in current dir *)
MGET ALICE.SRC.* (* download datasets matching pattern *)
MPUT *.txt (* upload every .txt from local dir *)
MPUT *.jcl 'ALICE.JCL.LIB(*)' (* PDS members — stem becomes member name *)
PROMPT (* toggle per-file prompt on/off *)
When PROMPT is on (default in many builds), FTP asks before each individual GET/PUT in the batch. Turn it off (PROMPT) before any large MGET in a script.
Passive vs active mode (firewalls)
In active mode the client tells the server which port to connect back to for data; the server initiates the data connection. This breaks when a firewall blocks inbound connections to the client. In passive mode the client initiates both control and data connections — the server publishes a data port and the client connects to it.
PASSIVE (* toggle PASV mode *)
SENDPASV ON (* always send PASV before transfer *)
SENDPASV OFF (* never — for sites that block PASV *)
In FTP.DATA:
PASSIVEDATACONN TRUE (* default to passive *)
DATABUFSIZE 180224 (* large socket buffers help over WAN *)
For z/OS as the FTP server behind a firewall, the firewall must NAT the PASV port range. Set PASSIVEDATAPORTS rangelow-rangehigh in FTP.DATA on the server and open that range in the firewall rule. Without this, passive transfers from external clients fail with 425 Can't open data connection.
FTPS — TLS configuration in depth
z/OS FTP supports both explicit FTPS (AUTH TLS after connect, default port 21) and implicit FTPS (TLS handshake first, default port 990). TLS is provided by the z/OS System SSL component; certificates live in a RACF keyring.
Client-side FTPS
FTP myhost.example.com SECURE (* explicit FTPS *)
FTP myhost.example.com SECURE PORT(990) (* implicit FTPS *)
In userid.FTP.DATA:
TLSMECHANISM ATTLS (* use AT-TLS policy *)
TLSMECHANISM TLS (* use System SSL directly *)
SECURE_FTP REQUIRED (* require TLS at connection time *)
SECURE_CTRLCONN PRIVATE (* require encrypted control conn *)
SECURE_DATACONN PRIVATE (* require encrypted data conn *)
SECURE_LOGIN REQUIRED (* refuse anonymous over cleartext *)
SECURE_PBSZ 0 (* protection buffer size — 0 for TLS *)
KEYRING ALICE/FTPCLIENT (* userid/keyring containing trust certs *)
CIPHERSUITE TLS_RSA_WITH_AES_256_CBC_SHA256
CIPHERSUITE TLS_RSA_WITH_AES_128_GCM_SHA256
Server-side FTPS
The server reads SYS1.TCPPARMS(FTPDATA) (or its alternate). The same SECURE_* keywords apply, plus:
EXTENSIONS AUTH_TLS (* advertise AUTH TLS on FEAT *)
TLSPORT 21 (* explicit *)
SECURE_CTRLCONN PRIVATE
SECURE_DATACONN PRIVATE
KEYRING FTPD/FTPDRING (* keyring for server certs *)
TLS 1.3 and FIPS 140-3 (z/OS 3.1 / 3.2)
z/OS 3.1 Communications Server adds AT-TLS support for TLS 1.3 including cached handshakes for faster reconnects. On z/OS 3.1 the combination AT-TLS + FIPS 140 mode + TLS 1.3 is not supported — FIPS 140-2 does not define TLS 1.3 cipher suites, so attempting the combination fails the handshake. z/OS 3.2 (GA 30 September 2025) adds FIPS 140-3 support, enabled via the FIPSMODE(140-3,INDICATE,FAIL(fail-option)) AT-TLS group action; FIPS 140-3 is compatible with TLS 1.3 but the ChaCha20 and ChaCha20-Poly1305 cipher suites remain disallowed — use AES-GCM/AES-CCM ciphers (e.g. TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256) only. Apply APAR PH59425 on 3.1 to ensure LE HEAPPOOLS64 is always enabled for AT-TLS, which materially improves TLS 1.3 connection throughput.
;; pagent policy fragment — TLS 1.3 with FIPS 140-3 (z/OS 3.2)
TTLSEnvironmentAction TLS_ENV_CLIENT
HandshakeRole Client
TTLSKeyringParms
Keyring FTPCLIENT/FTPCLIENTRING
TTLSCipherParms
V3CipherSuites4Char 1302 1303 ;; AES-256-GCM, AES-128-GCM
TTLSEnvironmentAdvancedParms
TLSv1.3 On
ApplicationControlled Off
FIPSMODE 140-3 INDICATE FAIL OFF
z/OS 3.2 also activates the SMF 1154 record subtype carrying FTP server JES access decisions, useful as compliance evidence for SITE FILETYPE=JES activity.
AT-TLS — application-transparent TLS
AT-TLS is the recommended modern approach: TCP/IP intercepts FTP traffic and applies TLS based on a Policy Agent rule, with no FTP-level configuration. The rule lives in pagent policy:
TTLSRule FTPCLIENT
LocalAddrRef ADDR_LOCAL
RemotePortRange 21
Jobname FTPCLIENT*
TTLSGroupActionRef TLS_GROUP
TTLSEnvironmentActionRef TLS_ENV_CLIENT
The FTP client doesn't need SECURE on the command line — AT-TLS wraps the socket transparently. The trade-off is debugging: when TLS fails, the FTP error is generic ("connection reset") and the real cause is in the pagent policy log.
NETRC — secure auto-login
userid.NETRC (or ~/.netrc in USS) stores credentials for auto-login. Always protect with RACF UACC(NONE) and an FB80 RECFM (or no DCB and rely on USS file permission 600).
MACHINE myhost.example.com LOGIN alice PASSWORD s3cr3t!
MACHINE prod.example.com LOGIN aliceprd PASSWORD prodS3cret
MACHINE qa.example.com LOGIN aliceqa PASSWORD qaS3cret
MACHINE default LOGIN anonymous PASSWORD alice@example.com
RACF-discreet alternative: store the password in an ICSF-encrypted dataset and have a wrapper REXX prepend the cleartext to the FTP session via SYSTSIN. Or use AT-TLS with client-certificate authentication so the password isn't needed at all.
Pairing with co:Z SFTP
Co:Z SFTP is an open-source SFTP client/server for z/OS distributed by Dovetailed Technologies. Use SFTP (SSH-based) when:
- The remote system only offers SFTP (most cloud providers, modern hosts)
- Firewall traversal requires a single TCP port (SFTP uses 22; FTP needs 21 plus PASV range)
- Audit/compliance requires SSH-key authentication
FTP is still preferable when:
- The remote is another z/OS and EBCDIC/JES integration is needed (
SITE FILETYPE=JES) - High-throughput LAN transfers benefit from block mode
- Existing JCL invokes
PGM=FTP
Co:Z SFTP supports the same //ddname syntax for dataset access and offers a dsmounter to expose datasets as USS files for tooling that expects a path. Sample co:Z SFTP batch JCL:
//ALICEJ13 JOB (ACCT),'SFTP UP',CLASS=A,MSGCLASS=X
//SFTP EXEC PGM=BPXBATCH,
// PARM='SH /usr/local/coz/bin/sftp-batch.sh'
//STDOUT DD SYSOUT=*
//STDERR DD SYSOUT=*
Real-world recipes
Push a build artifact from a Linux CI to a z/OS PDS
A CI runner uploads a freshly built load module member to a z/OS PDSE. Note TYPE I for binary, MODE B to preserve record structure on the receiving side, and SITE RECFM=U so the PDSE accepts the load module shape.
# Linux side - shell script
ftp -n myhost.example.com <<EOF
USER alice secret
quote SITE RECFM=U BLKSIZE=32760 DSNTYPE=LIBRARY DIR=0 PRI=10 SEC=5 CYL
TYPE I
MODE B
PUT alicepgm.load 'ALICE.PROD.LOAD(ALICEPGM)'
QUIT
EOF
Output:
Connected to myhost.example.com.
220-FTPMVS1 IBM FTP CS V2R5 at MYHOST, 12:04:18 on 2026-05-25.
331 Send password please.
230 ALICE is logged on. Working directory is "ALICE.".
200 SITE command was accepted
200 Representation type is Image
200 Data transfer mode is Block
200 Port request OK.
125 Storing data set ALICE.PROD.LOAD(ALICEPGM)
250 Transfer completed successfully.
221 Quit command received. Goodbye.
Retrieve SYSOUT for the latest run of a recurring job
A monitor script polls JES for the last instance of ALICEJ01 and copies its JESYSMSG (.3) for the daily run-log aggregator.
ftp> SITE FILETYPE=JES
ftp> SITE JESJOBNAME=ALICEJ01
ftp> SITE JESSTATUS=OUTPUT
ftp> DIR
ALICE ALICEJ01 JOB23456 OUTPUT A RC=0000 3 spool files
ftp> GET JOB23456.3 alicej01-jesmsg.txt
ftp> QUIT
Scripted batch FTP via JCL SYSIN cards
A nightly job pushes a daily extract to a remote SFTP-fronted FTP gateway. The credentials come from a RACF-protected NETRC.
//ALICEJ14 JOB (ACCT),'NIGHTLY FTP',CLASS=A,MSGCLASS=X,
// NOTIFY=&SYSUID
//FTP EXEC PGM=FTP,PARM='myhost.example.com (TIMEOUT 600',
// REGION=64M
//SYSPRINT DD SYSOUT=*
//OUTPUT DD SYSOUT=*
//NETRC DD DSN=ALICE.NETRC,DISP=SHR
//INPUT DD *
TYPE I
MODE B
LOCSITE RECFM=FB LRECL=200 BLKSIZE=0
SITE RECFM=FB LRECL=200 BLKSIZE=0 PRI=100 SEC=20 CYLINDERS
SITE STORCLAS=SCSTD MGMTCLAS=MCRETN30
PUT 'ALICE.DAILY.EXTRACT(+1)' /upload/daily/extract.dat
QUIT
/*
Pull every member of a remote PDS into a local directory
The remote z/OS exposes a PDS that you want mirrored as one file per member on the local filesystem.
ftp -n myhost.example.com <<EOF
USER alice secret
TYPE A
CD 'ALICE.PROD.PROCLIB'
DIR
PROMPT # turn off per-file confirm
MGET *
QUIT
EOF
Output:
Connected to myhost.example.com.
230 ALICE is logged on. Working directory is "ALICE.".
250 The working directory "ALICE.PROD.PROCLIB" is a partitioned data set
Name VV.MM Created Changed Size Init Mod Id
BACKUP01 01.04 2026/04/12 2026/05/02 09:13 42 42 0 ALICE
DAILYRUN 01.12 2026/01/03 2026/05/20 14:55 118 100 0 ALICE
WKLYRPT 01.07 2026/02/18 2026/05/18 07:21 88 88 0 ALICE
250 List completed successfully.
Interactive mode off.
125 Sending data set ALICE.PROD.PROCLIB(BACKUP01)
250 Transfer completed successfully.
125 Sending data set ALICE.PROD.PROCLIB(DAILYRUN)
250 Transfer completed successfully.
125 Sending data set ALICE.PROD.PROCLIB(WKLYRPT)
250 Transfer completed successfully.
221 Quit command received. Goodbye.
Submit JCL with parameterised symbolics from a Python script
A modern CI runner uses Python's ftplib to render a JCL template and submit it. The library is generic FTP; z/OS specifics come from SITE quotes.
import ftplib
jcl = open('template.jcl').read().format(
JOBNAME='ALICEJ15',
HLQ='ALICE',
RUNDATE='20260525',
)
ftp = ftplib.FTP('myhost.example.com')
ftp.login('alice', 'secret')
ftp.voidcmd('SITE FILETYPE=JES')
from io import BytesIO
buf = BytesIO(jcl.encode('cp1047'))
resp = ftp.storlines('STOR ALICEJ15.JCL', buf)
print(resp) # IT IS KNOWN TO JES AS JOB...
ftp.quit()
Round-trip a binary file between two z/OS systems
Two mainframes need to exchange a load module without translation. EBCDIC mode (TYPE E) skips translation entirely; combined with block mode the record structure of a PDS member is preserved.
TYPE E
MODE B
SITE RECFM=U BLKSIZE=32760 DSNTYPE=LIBRARY DIR=0 PRI=10 SEC=5 CYL
PUT 'ALICE.SRC.LOAD(ALICEPGM)' 'ALICE.TGT.LOAD(ALICEPGM)'
List active JES jobs and filter by class
SITE JESSTATUS=ALL shows input, active, and output queues simultaneously; combine with JESCLASS= for narrow filters.
ftp> SITE FILETYPE=JES
ftp> SITE JESJOBNAME=*
ftp> SITE JESOWNER=ALICE
ftp> SITE JESSTATUS=ACTIVE
ftp> DIR
ALICE ALICEJ20 JOB89001 ACTIVE - STC RC=---- 5 spool
ALICE ALICEJ21 JOB89015 ACTIVE - STC RC=---- 3 spool
More common pitfalls
- Wrong codepage in
TYPE A— IBM-1047 is the safe default but the[ ] { } \ ^ ~characters land on different EBCDIC bytes in 037 vs 1047. If two parties pick different EBCDIC code pages, the round-trip silently corrupts those characters. Standardise onSITE ENCODING=IBM-1047and document the choice. - Line endings on
TYPE Afrom Unix — Unix uses LF, EBCDIC datasets use no line terminator at all (records have fixed/variable length). FTP's TYPE A handles the conversion, but if the source has CRLF the server gets an extra CR that ends up asx0Dbyte at end of each record. Strip CRLF first or setSITE SBSENDEOL=NONE. PUTto a dataset that doesn't exist with no SITE — the server falls back to FTP.DATALOCSITEdefaults, which may not match the source. Always set SITE before PUT.- Dataset disposition — z/OS FTP server allocates target datasets with
DISP=NEW,CATLGunlessSITE NOFLUSHis set. A retry after a partial transfer can hitIGD17001Ibecause the failed attempt left a half-allocated dataset; either delete first or useAPPEND. FILETYPE=JESleft set — switching to JES for one submit and forgetting to switch back means subsequentPUTcommands try to submit them as JCL. Always pairSITE FILETYPE=JESwithSITE FILETYPE=SEQat the end of the block.- MGET overwriting local files — z/OS FTP overwrites local datasets silently. Use
STATUSto confirmSUNIQUE(store-unique) is on if you want unique-name protection. - Quoting traps in JCL
//INPUT DD *— JCL's in-stream delimiter/*ends the DD. If your FTP script contains a line starting with/*(a comment in another language), the JCL conversion truncates the script. Use//INPUT DD *,DLM=@@and end with@@. - Anonymous over cleartext — many sites still allow
LOGIN ANONYMOUSover cleartext FTP. RACF can be tightened withSECURE_LOGIN REQUIREDin FTP.DATA to refuse non-TLS logins. - AT-TLS rule misses — if the AT-TLS rule selector (jobname, port, address) doesn't match, the connection goes plaintext.
pagenttrace + Netstat ALL,IPPORT= help diagnose. SITE BLOCKSIZE=0on non-SMS volumes — system-determined BLKSIZE only works for SMS-managed datasets. On non-SMS volumes, explicitBLKSIZE=27920(or another half-track multiple) is required.- Firewall PASV port range mismatch — the server's
PASSIVEDATAPORTSrange must match the firewall NAT rules. Mismatch yields425 Can't open data connectiononly on transfer (the control connection works fine). - GET/PUT direction on
//INPUTcards — typoGUT(instead of GET/PUT) is silently accepted by some FTP clients as "Group" — the result is unpredictable. Always uppercase keywords and visually inspect script before run. - Member-name length — a PDS member name is 1–8 characters. A
MPUT *.txt 'ALICE.PDS(*)'where filenames have stems longer than 8 chars truncates silently. UseRENAMEfirst or shorter source filenames. TYPE Ito a text-format remote — uploading a fixed-block 80-byte file as binary to a Unix host produces an 80-byte-wrapped blob with no LF, often unreadable to text tooling. Use TYPE A for cross-platform text.MODE Bmid-session — switching mode after some transfers have happened can leave the data connection in an inconsistent state on some FTP server releases. Set MODE once at the top of the session.
See uss for USS file-system semantics that interact with FTP, iebcopy for PDS-aware copy alternatives, and tcpip for the TCP/IP stack and port-binding context.