Adobe made a S3 mock server you can simply run via docker

If you need to work with S3 locally without dealing directly with AWS, Adobe’s S3Mock is a fantastic solution. It supports the main S3 operations, eliminating the need to configure IAM or manage real cloud resources during development. You can spin it up via Docker like this:

# docker run --rm -p 9090:9090 -e initialBuckets=test -t adobe/s3mock
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0
Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts


  .-')              _   .-')                           .-. .-')
 ( OO ).           ( '.( OO )_                         \  ( OO )
(_)---\_) .-----.   ,--.   ,--.).-'),-----.    .-----. ,--. ,--.
/    _ | /  -.   \  |   `.'   |( OO'  .-.  '  '  .--./ |  .'   /
\  :` `. '-' _'  |  |         |/   |  | |  |  |  |('-. |      /,
 '..`''.)   |_  <   |  |'.'|  |\_) |  |\|  | /_) |OO  )|     ' _)
.-._)   \.-.  |  |  |  |   |  |  \ |  | |  | ||  |`-'| |  .   \
\       /\ `-'   /  |  |   |  |   `'  '-'  '(_'  '--'\ |  |\   \
 `-----'  `----''   `--'   `--'     `-----'    `-----' `--' '--'



2025-01-21T11:45:05.734Z INFO 1 --- [           main] c.a.testing.s3mock.S3MockApplication     : Starting S3MockApplication using Java 21.0.5 with PID 1 (/s3mock.jar started by root in /)
2025-01-21T11:45:05.742Z INFO 1 --- [           main] c.a.testing.s3mock.S3MockApplication     : No active profile set, falling back to 1 default profile: "default"
2025-01-21T11:45:06.990Z INFO 1 --- [           main] o.s.b.w.e.j.JettyServletWebServerFactory : Server initialized with port: 9191
2025-01-21T11:45:07.141Z INFO 1 --- [           main] org.eclipse.jetty.server.Server          : jetty-12.0.12; built: 2024-07-25T21:58:37.668Z; git: cc6f1b74db755fed228b50701ad967aeaa68e83f; jvm 21.0.5+11-alpine-r0
2025-01-21T11:45:07.197Z INFO 1 --- [           main] o.e.j.s.h.ContextHandler.application     : Initializing Spring embedded WebApplicationContext
2025-01-21T11:45:07.199Z INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1394 ms
Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
2025-01-21T11:45:07.378Z INFO 1 --- [           main] o.e.j.session.DefaultSessionIdManager    : Session workerName=node0
2025-01-21T11:45:07.392Z INFO 1 --- [           main] o.e.jetty.server.handler.ContextHandler  : Started osbwej.JettyEmbeddedWebAppContext@30aec673{application,/,b=file:/tmp/jetty-docbase.9191.3436685602967699509/,a=AVAILABLE,h=oeje10s.SessionHandler@549ac12c{STARTED}}
2025-01-21T11:45:07.393Z INFO 1 --- [           main] o.e.j.e.servlet.ServletContextHandler    : Started osbwej.JettyEmbeddedWebAppContext@30aec673{application,/,b=file:/tmp/jetty-docbase.9191.3436685602967699509/,a=AVAILABLE,h=oeje10s.SessionHandler@549ac12c{STARTED}}
2025-01-21T11:45:07.404Z INFO 1 --- [           main] org.eclipse.jetty.server.Server          : Started oejs.Server@39c85c1a{STARTING}[12.0.12,sto=0] @2879ms
2025-01-21T11:45:07.418Z INFO 1 --- [           main] c.a.t.s3mock.store.StoreConfiguration    : Successfully created "/s3mockroot" as root folder. Will retain files on exit: false
2025-01-21T11:45:07.499Z INFO 1 --- [           main] c.a.t.s3mock.store.StoreConfiguration    : Creating initial bucket test.
2025-01-21T11:45:07.880Z WARN 1 --- [           main] i.m.c.i.binder.jvm.JvmGcMetrics          : GC notifications will not be available because com.sun.management.GarbageCollectionNotificationInfo is not present
2025-01-21T11:45:07.934Z INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint beneath base path '/actuator'
2025-01-21T11:45:07.973Z INFO 1 --- [           main] o.e.j.s.h.ContextHandler.application     : Initializing Spring DispatcherServlet 'dispatcherServlet'
2025-01-21T11:45:07.973Z INFO 1 --- [           main] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2025-01-21T11:45:07.974Z INFO 1 --- [           main] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
2025-01-21T11:45:07.982Z INFO 1 --- [           main] o.e.jetty.util.ssl.SslContextFactory     : x509=X509@b5c902(selfsigned,h=[localhost, adobe s3mock],a=[],w=[]) for Server@7e747037[provider=null,keyStore=null,trustStore=null]

2025-01-21T11:45:08.085Z INFO 1 --- [           main] o.e.jetty.server.AbstractConnector       : Started SslValidatingServerConnector@184ff5d7{SSL, (ssl, http/1.1)}{0.0.0.0:9191}
2025-01-21T11:45:08.088Z INFO 1 --- [           main] o.e.jetty.server.AbstractConnector       : Started ServerConnector@645dc557{HTTP/1.1, (http/1.1)}{0.0.0.0:9090}
2025-01-21T11:45:08.090Z INFO 1 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer  : Jetty started on ports 9191 (ssl, http/1.1), 9090 (http/1.1) with context path '/'
2025-01-21T11:45:08.100Z INFO 1 --- [           main] c.a.testing.s3mock.S3MockApplication     : Started S3MockApplication in 2.864 seconds (process running for 3.574)

Once your server has booted, you can play around with the SDK or the AWS CLI, for example you can start by creating a bucket

# aws s3api create-bucket \
    --bucket my-bucket \
    --endpoint-url http://localhost:9090
# aws s3 ls / --endpoint-url http://localhost:9090
2025-03-11 13:38:25 my-bucket
2025-03-11 13:38:23 test

and follow along by uploading a file and listing the files within that bucket:

# aws s3 cp /etc/machine-id s3://my-bucket/ --endpoint-url http://localhost:9090
upload: ../../etc/machine-id to s3://my-bucket/machine-id
# aws s3 ls my-bucket/ --endpoint-url http://localhost:9090
2025-03-11 13:42:14 33 machine-id