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

text
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

text
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

TypeCommandUse when
ASCIITYPE AText files between mainframe and non-mainframe; translates EBCDIC↔ASCII
Binary/ImageTYPE B or TYPE ILoad modules, compiled objects, binary data; no translation
EBCDICTYPE EMainframe-to-mainframe text transfers; no translation
text
TYPE A    (* ASCII — default for most z/OS FTP servers *)
TYPE B    (* Binary/Image *)
TYPE E    (* EBCDIC *)

Tip: Always set TYPE B before 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:

text
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:

text
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:

text
SITE MGMTCLAS=STANDARD STORCLAS=FAST DATACLAS=FB80
SITE DATACLAS=VB255
SITE STORCLAS=PREMIUM

Space units:

text
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:

text
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:

text
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):

text
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:

text
GET PROCLIB(IEFBR14)        (* becomes userid.PROCLIB(IEFBR14) *)
PUT myfile DATA             (* becomes userid.DATA *)

PDS member transfers

Upload to a specific member:

text
PUT localfile 'MY.PDS.LIB(MEMBER)'

Upload multiple files — member names taken from filename stems:

text
MPUT *.cbl 'MY.COBOL.SRC(*)'
MPUT *.jcl 'MY.JCL.LIB(*)'

Download all members of a PDS:

text
CD 'MY.SOURCE.LIB'
DIR                          (* verify members *)
MGET *                       (* download all members as separate local files *)

Download one member:

text
GET 'MY.PDS.LIB(MEMBER)' member.txt

Submitting JCL and retrieving spool via FTP (FILETYPE=JES)

Switch to JES mode, submit, and retrieve output:

text
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 DDMeaning
.1JESMSGLG — JES message log
.2JESJCL — expanded JCL
.3JESYSMSG — JES/system messages
.4+Application SYSOUT DD statements in step order

Batch FTP via JCL

Standard batch download:

text
//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:

text
//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:

text
//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:

text
//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:

text
//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:

text
//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:

  1. userid.FTP.DATA (personal)
  2. FTP.DATA under current prefix
  3. SYS1.TCPPARMS(FTPDATA) (site-wide default)

Common FTP.DATA keywords:

text
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:

text
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:

text
FTP hostname SECURE
FTP hostname SECURE PORT(990)           (* implicit TLS on port 990 *)

In FTP.DATA for always-on TLS:

text
TLSMECHANISM  TLS
SECURE_CTRLCONN PRIVATE
SECURE_DATACONN PRIVATE

In batch JCL:

text
//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:

text
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:

text
GET 'PROD.PROCLIB(STARTED)' started.proc

Upload and replace a member:

text
SITE RECFM=FB LRECL=80
PUT deploy.jcl 'MY.JCL.LIB(DEPLOY)'

Transfer between two z/OS systems (EBCDIC, no translation):

text
TYPE E
GET 'REMOTE.DS' 'LOCAL.DS'

Bulk download from remote directory:

text
CD /u/myuser/exports
MGET *.csv

Retrieve multiple spool outputs from a batch job:

text
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:

text
PASSIVE
GET remotefile localfile

FTP return codes

RangeMeaning
1xxPositive preliminary — action initiated
2xxPositive completion — action succeeded
3xxPositive intermediate — more information needed
4xxTransient negative — temporary failure, retry may work
5xxPermanent negative — do not retry without fixing the cause

Common codes:

CodeMeaning
125Data connection open, transfer starting
150File status OK, opening data connection
200Command OK
220Service ready (server greeting)
226Transfer complete, closing data connection
230User logged in
250Requested action OK
257Pathname created
331Username OK, password required
425Can't open data connection
426Connection closed, transfer aborted
500Syntax error / command unrecognized
501Syntax error in parameters
530Not logged in / authentication failed
550File unavailable — not found, no RACF access, or wrong filetype
553File name not allowed

Troubleshooting

SymptomLikely causeFix
425 Can't open data connectionFirewall blocking data portIssue PASSIVE command or open FTP data port range
Garbled text after transferWrong transfer typeUse TYPE A for text, TYPE B for binary
Dataset not found (550)Missing quotes on absolute nameAdd single quotes around fully qualified names
Userid prefix appended unexpectedlyUnquoted dataset nameQuote the dataset name: 'SYS1.PROCLIB'
ABEND in batch FTPServer-side or allocation errorCheck SYSPRINT for response codes and messages
Permission denied (550)RACF access deniedVerify dataset profile and userid permissions
TLS handshake failureCertificate not trustedCheck AT-TLS policy; may need to add server cert to keyring
Transfer complete but data wrongBinary file sent as ASCIIRe-transfer with TYPE B
Member truncatedWrong LRECLUse SITE LRECL=nnn matching source LRECL
Batch job gets RC 12FTP authentication or allocation failureCheck 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

KeywordEquivalent JCLNotes
RECFM=FB / VB / U / FBA / VBADCB=(RECFM=…)Fixed/variable, blocked, ANSI carriage control, undefined
LRECL=nnnLRECL=nnnLogical record length; for VB include the 4-byte RDW
BLKSIZE=nnnBLKSIZE=nnnBLKSIZE=0 lets SMS pick a system-determined optimum
BLOCKSIZE=nnnBLKSIZE=nnnSynonym accepted by some FTP server levels
BLOCKS / CYLINDERS / TRACKSSPACE=(unit,…)Allocation unit
PRI=nnnSPACE=(unit,(nnn,sec))Primary extent
SEC=nnnSPACE=(unit,(prim,nnn))Secondary extent
DIRECTORY=nnnSPACE=(unit,(p,s,nnn))PDS directory blocks
PDS / PDSE / DSNTYPE=LIBRARYDSNTYPE=LIBRARYPDS vs PDSE
LARGEDSNTYPE=LARGELarge-format sequential (>65535 tracks)
EXTPREF / EXTREQDSNTYPE=EXTPREF/EXTREQExtended-format preferred / required
RETPD=nnnRETPD=nnnRetention period in days
EXPDT=yyyy/dddEXPDT=yyyy/dddExplicit expiration date
STORCLAS=nameSTORCLAS=SMS storage class
MGMTCLAS=nameMGMTCLAS=SMS management class
DATACLAS=nameDATACLAS=SMS data class
UNIT=typeUNIT=Device class (SYSDA, TAPE, 3390)
VOLUME=serVOL=SER=Specific volume serial
AVGREC=AVGREC=Allocation in records (U/K/M units)
FILETYPE=SEQ / JES / SQLn/a — FTP-onlyServer 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.

ValueMeaning
SEQDefault — transfers move bytes between local and remote sequential datasets / PDS members
JESPUT submits the local file as JCL; GET retrieves spool output; DIR lists your jobs
SQLThe local file is a SQL statement passed to DB2; the result set is returned as the transfer
text
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.

text
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

text
(* 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.

text
(* 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.

text
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.

text
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=.

text
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.

text
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.

text
PASSIVE                            (* toggle PASV mode *)
SENDPASV ON                        (* always send PASV before transfer *)
SENDPASV OFF                       (* never — for sites that block PASV *)

In FTP.DATA:

text
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

text
FTP myhost.example.com SECURE              (* explicit FTPS *)
FTP myhost.example.com SECURE PORT(990)    (* implicit FTPS *)

In userid.FTP.DATA:

text
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:

text
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.

text
;; 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:

text
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).

text
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:

text
//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.

bash
# 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:

text
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.

text
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.

text
//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.

bash
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:

text
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.

python
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.

text
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.

text
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

  1. 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 on SITE ENCODING=IBM-1047 and document the choice.
  2. Line endings on TYPE A from 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 as x0D byte at end of each record. Strip CRLF first or set SITE SBSENDEOL=NONE.
  3. PUT to a dataset that doesn't exist with no SITE — the server falls back to FTP.DATA LOCSITE defaults, which may not match the source. Always set SITE before PUT.
  4. Dataset disposition — z/OS FTP server allocates target datasets with DISP=NEW,CATLG unless SITE NOFLUSH is set. A retry after a partial transfer can hit IGD17001I because the failed attempt left a half-allocated dataset; either delete first or use APPEND.
  5. FILETYPE=JES left set — switching to JES for one submit and forgetting to switch back means subsequent PUT commands try to submit them as JCL. Always pair SITE FILETYPE=JES with SITE FILETYPE=SEQ at the end of the block.
  6. MGET overwriting local files — z/OS FTP overwrites local datasets silently. Use STATUS to confirm SUNIQUE (store-unique) is on if you want unique-name protection.
  7. 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 @@.
  8. Anonymous over cleartext — many sites still allow LOGIN ANONYMOUS over cleartext FTP. RACF can be tightened with SECURE_LOGIN REQUIRED in FTP.DATA to refuse non-TLS logins.
  9. AT-TLS rule misses — if the AT-TLS rule selector (jobname, port, address) doesn't match, the connection goes plaintext. pagent trace + Netstat ALL,IPPORT= help diagnose.
  10. SITE BLOCKSIZE=0 on non-SMS volumes — system-determined BLKSIZE only works for SMS-managed datasets. On non-SMS volumes, explicit BLKSIZE=27920 (or another half-track multiple) is required.
  11. Firewall PASV port range mismatch — the server's PASSIVEDATAPORTS range must match the firewall NAT rules. Mismatch yields 425 Can't open data connection only on transfer (the control connection works fine).
  12. GET/PUT direction on //INPUT cards — typo GUT (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.
  13. 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. Use RENAME first or shorter source filenames.
  14. TYPE I to 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.
  15. MODE B mid-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.

Sources