Forked from awsm which is Copyright (c) Solvuu, Inc. Released under the MIT license.
Pure OCaml client for AWS. Code is auto-generated for all services based on the API declared in botocore. Higher level functions are sometimes provided on top of this base to (e.g.) support multi-part uploads to S3.
Nearly all of AWS’s 400+ APIs are supported (generated from botocore 1.43.9, freshened 2026-05-17). Three I/O flavors:
awso-eio - Effects based Eio backendawso-lwt — Lwt backendawso-async — Jane Street Async backendawso-sync — synchronous (blocking) backend; uses libcurl under the hoodThere is additionally a command-line tool awso-cli for
all APIs composed using Core.Command.
opam install awso-eio # Eio
opam install awso-lwt # Lwt
opam install awso-async # Async
opam install awso-sync # synchronous (blocking)
opam install awso-cli # umbrella CLI binary
Note: the AWS surface is massive and this package can take a lengthy amount of time and space to build.
See the examples directory.
Here is a short example that lists all EC2 instances using Eio:
(* ec2_describe_instances.ml *)
module Ec2 = Awso_ec2_eio
let print_row a b c d = Printf.printf "%-16s %-15s %-39s %-20s\n" a b c d
let print_instance instance =
let name =
Option.bind instance.Ec2.Instance.tags (fun tags ->
List.find_map
(function
| { Ec2.Tag.key = Some "Name"; value = Some v } -> Some v
| _ -> None)
tags)
in
let instance_type =
match instance.instanceType with
| Some it -> Ec2.InstanceType.to_string it
| None -> ""
in
print_row
instance_type
(Option.value instance.publicIpAddress ~default:"")
(Option.value instance.ipv6Address ~default:"")
(Option.value name ~default:"")
;;
let main env =
let cfg = Awso_eio.Cfg.get_exn ~env () in
match Ec2.describe_instances ~cfg (Ec2.DescribeInstancesRequest.make ()) with
| Error e ->
failwith
(Printf.sprintf
"Ec2.describe_instances: %s"
(Yojson.Safe.to_string (Ec2.Ec2_error.to_json e)))
| Ok { reservations; _ } -> (
let instances =
reservations
|> Option.value ~default:[]
|> List.concat_map (function
| { Ec2.Reservation.instances = None; _ } -> []
| { instances = Some instances; _ } -> instances)
in
match instances with
| [] -> print_endline "no instances"
| instances ->
print_row "instance-type" "public ipv4" "public ipv6" "name";
print_row
(String.make 16 '-')
(String.make 15 '-')
(String.make 39 '-')
(String.make 20 '-');
List.iter print_instance instances)
;;
let () = Eio_main.run main ;;
; dune
(executable
(name ec2_describe_instances)
(libraries awso-eio.ec2 eio_main))
Make sure to populate ~/.aws/config and ~/.aws/credentials in the usual way, then:
dune exec ./ec2_describe_instances.exe
| Path | Purpose | Shipped to opam? |
|---|---|---|
lib/runtime/awso/ |
Core runtime: auth, HTTP, config, regions | Yes (awso) |
lib/runtime/eio/ |
Eio backend | Yes (awso-eio) |
lib/runtime/async/ |
Async backend | Yes (awso-async) |
lib/runtime/lwt/ |
Lwt backend | Yes (awso-lwt) |
lib/runtime/sync/ |
Synchronous (blocking) backend, libcurl-based | Yes (awso-sync) |
lib/common/ |
Jane Street Core compatibility shim | Yes (awso-common) |
aws/<service>/ |
Auto-generated per-service bindings (~400 services) | Yes (under awso, awso-async, awso-lwt, awso-sync) |
awso-cli/ |
A bit like the Python aws cli | Yes (awso-cli) |
lib/codegen/ |
The code generator that produces aws/<service>/ from botocore JSON |
No: private library |
vendor/botocore/ |
Vendored botocore JSON used by the codegen | No |
dogfood/ |
Important maintainer tools that use awso itself |
No |
examples/ |
Example programs | No, not installed |
aws/ tree committed to git?aws/<service>/ contains roughly 400 services worth of generated OCaml. We
commit it on purpose so that opam install awso-async (or any sibling package)
never has to run the codegen at install time. End users get a ~25 dependency
build instead of ~50+: ppxlib, sedlex, ocamlgraph, and the rest of the
codegen toolchain stay private to maintainers. The decision trades repo size
for install-time simplicity and predictability.
Regeneration is a maintainer concern, but you can re-generate yourself like so:
make generate-code
After regenerating, commit the resulting diff alongside whatever change prompted it.
Generate API docs locally with opam install odoc then make doc.