Modern COBOL runs on three platform families, each with a different integration toolchain:
| Platform | HTTP/REST Surface | JSON | Container | Notes |
|---|---|---|---|---|
| IBM z/OS + CICS | CICS Web Services, z/OS Connect EE | DFHJS2LS / JSON assistant | zCX (LinuxONE) | Primary mainframe stack |
| IBM z/OS + IMS | IMS Connect, z/OS Connect EE | IMS Universal Drivers | zCX | Hierarchical DB coupling |
| GnuCOBOL / open-source | libcurl via C interop, or wrapper programs | Hand-parse or cJSON via CALL | Standard Linux containers | Full Linux ecosystem available |
| Micro Focus / ACUCOBOL | Enterprise Server REST handlers | JSON intrinsics (MF 10+) | Docker-compatible | ISV-extended runtime |
z/OS Connect Enterprise Edition exposes external REST APIs as COBOL-callable service archives (.sar). The generated COBOL copybook maps JSON fields to a data structure; the program calls the service via INVOKE or standard CALL linkage depending on configuration.
* Generated copybook defines request/response structures
COPY ACCTINQRQ.
WORKING-STORAGE SECTION.
01 WS-REQUEST.
COPY ACCTINQRQ-REQUEST.
01 WS-RESPONSE.
COPY ACCTINQRQ-RESPONSE.
01 WS-HTTP-STATUS PIC 9(3).
PROCEDURE DIVISION.
MOVE 'ACC-12345' TO RQ-ACCOUNT-ID
CALL 'ACCTINQRQ' USING
WS-REQUEST
WS-RESPONSE
WS-HTTP-STATUS
IF WS-HTTP-STATUS = 200
MOVE RS-BALANCE TO WS-DISPLAY-BALANCE
ELSE
PERFORM HANDLE-HTTP-ERROR
END-IF.
GnuCOBOL can call C functions directly. A thin C wrapper around libcurl exposes a COBOL-friendly interface. The wrapper receives a URL, optional request body, and a response buffer; COBOL handles only the data layer.
/* cobol_http.c */ #include <curl/curl.h> #include <string.h> typedef struct { char *buf; size_t len; } Buf; static size_t write_cb(char *ptr, size_t sz, size_t n, Buf *b) { size_t total = sz * n; memcpy(b->buf + b->len, ptr, total); b->len += total; return total; } /* COBOL calls: CALL 'http_get' USING url url-len resp resp-len status */ void http_get(const char *url, int *url_len, char *resp_buf, int *resp_len, long *status) { CURL *h = curl_easy_init(); Buf b = { resp_buf, 0 }; curl_easy_setopt(h, CURLOPT_URL, url); curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(h, CURLOPT_WRITEDATA, &b); curl_easy_perform(h); curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, status); *resp_len = (int)b.len; curl_easy_cleanup(h); }
WORKING-STORAGE SECTION.
01 WS-URL PIC X(512) VALUE
'https://api.example.com/v1/accounts'.
01 WS-URL-LEN PIC 9(4) COMP.
01 WS-RESP-BUF PIC X(65536).
01 WS-RESP-LEN PIC 9(6) COMP.
01 WS-HTTP-STATUS PIC 9(4) COMP.
PROCEDURE DIVISION.
MOVE FUNCTION LENGTH(WS-URL) TO WS-URL-LEN
CALL 'http_get' USING
WS-URL
WS-URL-LEN
WS-RESP-BUF
WS-RESP-LEN
WS-HTTP-STATUS
IF WS-HTTP-STATUS = 200
PERFORM PARSE-JSON-RESPONSE
END-IF.
WS-RESP-BUF silently truncates. Either size the buffer conservatively large or stream in chunks from the C side.
CICS 3.1+ can front-end any COBOL program as an HTTP/REST endpoint. The pipeline mechanism transforms inbound HTTP to a COMMAREA or channel/container layout, dispatches to the COBOL program, and transforms the response back.
* CICS resource definitions — applied via CEDA or CICSPlex URIMAP(ACCTURI) GROUP(APIGRP) HOST(*) PATH(/api/v1/accounts/*) USAGE(SERVER) WEBSERVICE(ACCTWS) WEBSERVICE(ACCTWS) GROUP(APIGRP) PIPELINE(RESTPIPE) PROGRAM(ACCTINQ) PGMINTERFACE(COMMAREA)
WORKING-STORAGE SECTION.
01 WS-METHOD PIC X(8).
01 WS-PATH-INFO PIC X(128).
01 WS-RESP-STATUS PIC 9(3) VALUE 200.
01 WS-RESP-BODY PIC X(32768).
PROCEDURE DIVISION.
EXEC CICS
GET HTTP METHOD
INTO(WS-METHOD)
END-EXEC
EXEC CICS
EXTRACT HTTPS
PATHINFORMATION(WS-PATH-INFO)
END-EXEC
EVALUATE TRUE
WHEN WS-METHOD = 'GET '
PERFORM HANDLE-GET
WHEN WS-METHOD = 'POST '
PERFORM HANDLE-POST
WHEN OTHER
MOVE 405 TO WS-RESP-STATUS
END-EVALUATE
EXEC CICS
WEB SEND
FROM(WS-RESP-BODY)
STATUSCODE(WS-RESP-STATUS)
MEDIATYPE('application/json')
END-EXEC.
z/OS Connect EE decouples the API contract from the COBOL program entirely. A .sar (service archive) is deployed to z/OS Connect; it maps a REST path/method to a CICS, IMS, or batch program by name. The COBOL program sees only a typed COMMAREA — it has no HTTP awareness.
For non-mainframe deployments, two patterns apply. In the CGI pattern, a web server (nginx + fcgiwrap, or Apache) invokes the COBOL executable per request; stdin/stdout carry the body. In the sidecar pattern, a thin Go or Python service receives HTTP, serialises the payload to a flat file or shared memory, forks the COBOL binary, reads the output, and returns the response. The sidecar gives better lifecycle control and avoids per-process overhead.
Micro Focus COBOL 10.0+ provides JSON GENERATE and JSON PARSE as native statements. Field names default to the COBOL data-name; use SUPPRESS to omit null/zero fields.
WORKING-STORAGE SECTION.
01 ACCOUNT-RECORD.
05 ACCT-ID PIC X(12) VALUE 'ACC-12345'.
05 ACCT-BALANCE PIC S9(11)V99 COMP-3.
05 ACCT-CURRENCY PIC X(3) VALUE 'USD'.
01 WS-JSON-OUT PIC X(4096).
01 WS-JSON-COUNT PIC 9(6) COMP.
PROCEDURE DIVISION.
MOVE 10250.75 TO ACCT-BALANCE
JSON GENERATE WS-JSON-OUT
FROM ACCOUNT-RECORD
COUNT IN WS-JSON-COUNT
SUPPRESS ZEROS
*> Output: {"acct-id":"ACC-12345","acct-balance":10250.75,"acct-currency":"USD"}
WORKING-STORAGE SECTION.
01 WS-JSON-IN PIC X(4096).
01 ACCOUNT-RECORD.
05 ACCT-ID PIC X(12).
05 ACCT-BALANCE PIC S9(11)V99 COMP-3.
01 WS-PARSE-ERR PIC 9(4) COMP.
PROCEDURE DIVISION.
MOVE '{"acct-id":"ACC-12345","acct-balance":10250.75}'
TO WS-JSON-IN
JSON PARSE WS-JSON-IN
INTO ACCOUNT-RECORD
ON EXCEPTION
MOVE JSON-CODE TO WS-PARSE-ERR
PERFORM LOG-JSON-ERROR
END-JSON.
On z/OS CICS, the JSON Schema Assistant (DFHJS2LS utility) generates COBOL copybooks from a JSON schema. The copybook is then used with EXEC CICS GET CONTAINER or PUT CONTAINER to exchange data via named channels. Manual JSON string construction is not required.
WORKING-STORAGE SECTION.
01 WS-CHANNEL PIC X(16) VALUE 'ACCT-CHANNEL'.
01 WS-CONTAINER PIC X(16) VALUE 'DFHWS-DATA'.
COPY ACCTJSON. *> generated by DFHJS2LS
PROCEDURE DIVISION.
EXEC CICS
GET CONTAINER(WS-CONTAINER)
CHANNEL(WS-CHANNEL)
INTO(ACCT-JSON-AREA)
FLENGTH(WS-JSON-LEN)
END-EXEC
*> Copybook fields populated; proceed with business logic
MOVE ACCT-JSON-BALANCE TO WS-WORKING-BALANCE.
GnuCOBOL has no native JSON statement. Two options: write a minimal key-value parser using UNSTRING and INSPECT, or link against a C JSON library (cJSON, jansson) via CALL. For production use, the C library path is strongly preferred — hand-parsing is fragile against whitespace variation, nested arrays, or escaped characters.
INSPECT, UNSTRING) in production. Any nested object, unicode escape, or RFC 8259-compliant whitespace variant will break the parser silently and corrupt data without raising an error condition.
ISO COBOL 2002 and later define XML GENERATE and XML PARSE as standard statements. Both IBM Enterprise COBOL and Micro Focus implement them. GnuCOBOL 3.2+ has partial support.
WORKING-STORAGE SECTION.
01 ORDER-RECORD.
05 ORD-ID PIC X(10) VALUE 'ORD-9001'.
05 ORD-AMOUNT PIC S9(9)V99 COMP-3 VALUE 1500.00.
05 ORD-STATUS PIC X(8) VALUE 'PENDING'.
01 WS-XML-DOC PIC X(4096).
01 WS-XML-LEN PIC 9(6) COMP.
PROCEDURE DIVISION.
XML GENERATE WS-XML-DOC
FROM ORDER-RECORD
COUNT IN WS-XML-LEN
NAMESPACE 'https://example.com/orders'
NAMESPACE-PREFIX 'ord'
.
*> Produces:
*> <ord:ORDER-RECORD xmlns:ord="https://example.com/orders">
*> <ord:ORD-ID>ORD-9001</ord:ORD-ID>
*> <ord:ORD-AMOUNT>1500.00</ord:ORD-AMOUNT>
*> <ord:ORD-STATUS>PENDING</ord:ORD-STATUS>
*> </ord:ORDER-RECORD>
WORKING-STORAGE SECTION.
01 WS-XML-EVENT PIC X(30).
01 WS-XML-VALUE PIC X(512).
01 WS-TARGET-AMT PIC S9(9)V99 COMP-3.
PROCEDURE DIVISION.
XML PARSE WS-XML-DOC
PROCESSING PROCEDURE XML-HANDLER
ON EXCEPTION
PERFORM LOG-XML-ERROR
END-XML.
XML-HANDLER.
EVALUATE XML-EVENT
WHEN 'CONTENT-CHARACTER'
IF WS-LAST-ELEMENT = 'ORD-AMOUNT'
MOVE XML-TEXT TO WS-TARGET-AMT
END-IF
WHEN 'START-OF-ELEMENT'
MOVE XML-TEXT TO WS-LAST-ELEMENT
END-EVALUATE.
XML-EVENT, XML-TEXT, XML-NAMESPACE, and XML-NAMESPACE-PREFIX are COBOL special registers populated by the runtime during the parse callback. They do not need to be declared in Working-Storage.
IBM MQ provides a COBOL-callable interface via the MQI (Message Queue Interface). All MQ verbs are standard CALL statements against the MQ stub library. The primary calls are MQCONN, MQOPEN, MQPUT, MQGET, MQCLOSE, and MQDISC.
WORKING-STORAGE SECTION.
COPY CMQV. *> MQ constants copybook
COPY CMQMDV. *> Message Descriptor copybook
COPY CMQPMO. *> Put Message Options copybook
01 WS-HCONN PIC S9(9) COMP.
01 WS-HOBJ PIC S9(9) COMP.
01 WS-CC PIC S9(9) COMP.
01 WS-RC PIC S9(9) COMP.
01 WS-MSG-BUF PIC X(4096).
01 WS-MSG-LEN PIC S9(9) COMP.
PROCEDURE DIVISION.
CALL 'MQCONN' USING
'QMGR01 ' WS-HCONN WS-CC WS-RC
MOVE MQOO-OUTPUT TO MQOD-OPTIONS
MOVE 'ORDER.QUEUE ' TO MQOD-OBJECTNAME
CALL 'MQOPEN' USING
WS-HCONN MQOD MQOD-OPTIONS
WS-HOBJ WS-CC WS-RC
MOVE MQMT-PERSISTENT TO MQMD-MSGTYPE
MOVE WS-PAYLOAD TO WS-MSG-BUF
MOVE LENGTH OF WS-PAYLOAD TO WS-MSG-LEN
CALL 'MQPUT' USING
WS-HCONN WS-HOBJ MQMD MQPMO
WS-MSG-LEN WS-MSG-BUF
WS-CC WS-RC
IF WS-CC > MQCC-WARNING
PERFORM HANDLE-MQ-ERROR
END-IF.
COBOL programs do not directly call Kafka client libraries. The standard pattern is a sidecar bridge: a JVM or Go process subscribes to Kafka topics, materialises messages as flat files or shared memory segments, and triggers the COBOL program (via JCL, EXEC CICS LINK, or process fork). Results are written back to a reply topic by the bridge.
| Component | Role | Interface to COBOL |
|---|---|---|
| Kafka bridge (Go/JVM) | Consumer + producer | Flat file, pipe, or shared mem |
| COBOL program | Business logic | Standard I/O or COMMAREA |
| Schema Registry | Avro/Protobuf schema | Bridge deserialises; COBOL sees flat layout |
| IBM MQ ↔ Kafka bridge | MQ events to Kafka | Transparent; COBOL uses MQ only |
A COBOL monolith is rarely decomposed into COBOL microservices — the runtime overhead and operational complexity outweigh the benefit. The practical decomposition patterns expose COBOL logic as a service and delegate new capabilities to lightweight services written in other languages.
Route new HTTP traffic through an API gateway. Point routes for new features to modern services; point legacy routes to a CICS/IMS COBOL backend via z/OS Connect EE. Incrementally migrate routes as COBOL functionality is re-implemented. The COBOL layer shrinks over time without a single cutover.
# New payment service (Go) location /api/v2/payments/ { proxy_pass http://payments-svc:8080/; } # Legacy account inquiry (COBOL via z/OS Connect) location /api/v1/accounts/ { proxy_pass https://zosconnect.internal:9443/accounts/; proxy_ssl_certificate /etc/ssl/client.crt; proxy_ssl_certificate_key /etc/ssl/client.key; }
Place a translation service between modern microservices and the COBOL backend. The ACL maps domain models (rich JSON objects, ISO 8601 dates, UTF-8 strings) to COBOL-native formats (packed decimal, EBCDIC, Julian dates, fixed-width fields). Neither side needs to know the other's representation.
When containerised (see §8), a COBOL program can be a first-class microservice: a single executable exposed via a sidecar HTTP adapter, registered in a service mesh, horizontally scalable. This pattern works when:
# Build stage FROM ubuntu:24.04 AS builder RUN apt-get update && apt-get install -y gnucobol libcurl4-openssl-dev COPY src/ /app/src/ COPY cobol_http.c /app/ WORKDIR /app RUN gcc -shared -fPIC -o libcobolhttp.so cobol_http.c -lcurl RUN cobc -x -free -o acctinq src/acctinq.cbl \ -L. -lcobolhttp # Runtime stage FROM ubuntu:24.04 RUN apt-get update && apt-get install -y libcob4 libcurl4 COPY --from=builder /app/acctinq /usr/local/bin/acctinq COPY --from=builder /app/libcobolhttp.so /usr/local/lib/ RUN ldconfig COPY adapter/main.go /app/ # HTTP adapter EXPOSE 8080 CMD ["/app/http-adapter"]
apiVersion: apps/v1 kind: Deployment metadata: name: acctinq-cobol spec: replicas: 3 selector: matchLabels: app: acctinq-cobol template: metadata: labels: app: acctinq-cobol spec: containers: - name: cobol-svc image: registry.example.com/acctinq-cobol:1.4.2 ports: - containerPort: 8080 env: - name: DB_HOST valueFrom: secretKeyRef: name: cobol-secrets key: db-host resources: requests: { cpu: "100m", memory: "64Mi" } limits: { cpu: "500m", memory: "256Mi" } readinessProbe: httpGet: { path: /healthz, port: 8080 } initialDelaySeconds: 2 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: acctinq-cobol spec: selector: { app: acctinq-cobol } ports: - port: 80 targetPort: 8080
IBM zCX (z/OS Container Extensions) runs Docker containers on z/OS hardware using Linux on IBM Z. COBOL programs compiled for Linux/s390x run in zCX containers and share the z/OS network fabric, enabling low-latency access to CICS and IMS resources without leaving the mainframe. The deployment model is standard Docker; images must target linux/s390x.
# Build and push for zCX (s390x architecture) docker buildx build \ --platform linux/s390x \ --tag registry.example.com/acctinq-cobol:1.4.2 \ --push .
GnuCOBOL binaries run in AWS Lambda via a custom runtime. The runtime bootstrap script invokes the COBOL binary per event, passing the JSON payload on stdin, reading the response from stdout. Cold start for a GnuCOBOL binary is typically under 50ms; the runtime layer adds ~2MB to the deployment package.
#!/bin/sh # bootstrap — Lambda custom runtime entrypoint set -e while true; do # Poll Lambda runtime API for next event RESPONSE=$(curl -s -f \ "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") REQUEST_ID=$(curl -sI \ "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next" \ | grep Lambda-Runtime-Aws-Request-Id \ | awk '{print $2}' | tr -d '\r') # Invoke COBOL program; pipe JSON in/out RESULT=$(echo "${RESPONSE}" | /var/task/acctinq) # Post result back to Lambda runtime curl -s -X POST \ "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${REQUEST_ID}/response" \ -d "${RESULT}" done
COBOL programs do not perform TLS themselves. TLS is terminated at the edge (load balancer, API gateway, or sidecar such as Envoy). Intra-cluster traffic may be mTLS via the service mesh; the COBOL binary communicates over plaintext within the pod/container.
Token validation is handled upstream. For z/OS, z/OS Connect EE validates JWT tokens via SAF/RACF before dispatching to the COBOL program. For containerised deployments, an Envoy sidecar or API gateway (Kong, AWS API Gateway) validates the token and passes claims as HTTP headers. The COBOL program reads the headers from its input channel and applies coarse-grained authorisation checks based on claim values.
WORKING-STORAGE SECTION.
01 WS-USER-ROLE PIC X(32).
01 WS-HEADER-LEN PIC S9(9) COMP VALUE 32.
PROCEDURE DIVISION.
EXEC CICS
GET HTTP HEADER 'X-User-Role'
INTO(WS-USER-ROLE)
FLENGTH(WS-HEADER-LEN)
NOHANDLE
END-EXEC
IF WS-USER-ROLE <> 'ADMIN '
AND WS-USER-ROLE <> 'ANALYST '
PERFORM SEND-403-FORBIDDEN
STOP RUN
END-IF.
| Deployment | Recommended pattern |
|---|---|
| z/OS CICS | RACF keyring; credential passed via CICS resource definitions, not hard-coded |
| Kubernetes | Kubernetes Secrets mounted as env vars or files; Vault agent sidecar for rotation |
| AWS Lambda | AWS Secrets Manager; retrieved in bootstrap, cached for process lifetime |
| GnuCOBOL standalone | Environment variables; no plaintext in COBOL source or JCL |
VALUE clauses or JCL PARM fields is common in legacy code. Any modernisation effort must audit for this pattern — credentials embedded in source are committed to version control and exposed in dumps and SMF records.
| Task | z/OS CICS/IMS | Micro Focus | GnuCOBOL |
|---|---|---|---|
| Outbound HTTP GET | z/OS Connect EE outbound SAR | Enterprise Server REST client | C wrapper → libcurl |
| Expose as REST endpoint | CICS URIMAP + WEBSERVICE or z/OS Connect EE |
Enterprise Server listener | CGI or sidecar HTTP adapter |
| JSON generate | DFHJS2LS copybook + containers | JSON GENERATE (native) |
cJSON via CALL |
| JSON parse | DFHJS2LS copybook + containers | JSON PARSE (native) |
cJSON via CALL |
| XML generate | XML GENERATE (IBM COBOL) |
XML GENERATE |
Partial (GnuCOBOL 3.2+) |
| XML parse | XML PARSE + callback |
XML PARSE + callback |
Partial (GnuCOBOL 3.2+) |
| Message queue | IBM MQ MQI (MQPUT/MQGET) | IBM MQ or MSMQ via MQI | IBM MQ MQI or C bridge |
| Kafka | MQ-Kafka bridge (IBM) | MQ-Kafka bridge | Sidecar bridge (Go/JVM) |
| Container deployment | zCX (linux/s390x) | Docker (x86-64) | Docker (any arch) |
| Serverless | N/A (CICS runtime bound) | Limited | AWS Lambda custom runtime |
| TLS | AT-TLS (z/OS policy) | Enterprise Server SSL config | Envoy sidecar / LB termination |
| JWT / OAuth 2.0 | z/OS Connect EE + RACF | Gateway upstream | API gateway / Envoy upstream |
| Secrets management | RACF keyring | Vault or env vars | Vault / K8s Secrets / env vars |