Files
shadowsocks-libev/docker/static
Max Lv bcb3731968 C++20 port 1/14: build, ownership core and allocation audit (#3065)
* Enable C++20 alongside C11 in the build

Add CXX to the project with C++20 (no extensions) and build C++ sources
with -Wextra -fno-exceptions -fno-rtti -fno-threadsafe-statics on top of
the existing warning flags. Give every toolchain a C++ compiler: the zig
Windows toolchain, the static macOS/Windows CI jobs, the static musl
image (clang++ plus libstdc++ headers) and the RPM spec.

* Add C++20 ownership and allocation core

Header-only primitives for the no-heap-after-startup design:
Result/Status, Region, ObjectPool with PoolPtr (std::unique_ptr with a
pool deleter), generation-checked Handle and asynchronous close support,
intrusive RefPtr, FixedBuffer, MonotonicArena, FixedHashMap and FixedLru,
IntrusiveList, and a bounded FixedHeap for third-party allocator hooks.

Unit tests cover each primitive (test_core_*). scripts/check_no_heap.py
rejects heap allocation in C++ sources under src/ and runs as a ctest.
clang-tidy now also analyses C++ sources and the core headers.

* Add heap allocation audit for relayed connections

alloc_counter is a preloadable malloc counter (glibc, macOS) that writes
a snapshot on SIGUSR2. measure_alloc.py warms ss-local and ss-server up,
snapshots both, relays more connections and reports allocations per
connection. It runs report-only as the alloc_audit_report ctest.
interop.peers() now yields the started processes.

* Document the C++20 port and allocation baseline

docs/cpp-core.md describes the language profile, the core primitives,
the phase plan and the measured C baseline (77-89 allocations per
connection in ss-server, 27-39 in ss-local). The compatibility contract
now keeps the C11 public header instead of a C11 code base. CLAUDE.md
drops stale libev, submodule and gnu99 references.

* Fix CI for the C++ core: libstdc++ headers, static unwinder, newer clang

- docker/static: Alpine's libstdc++ headers come with g++, and the C++
  tests' static libstdc++ needs an unwinder that compiler-rt lacks; add
  g++ and llvm-libunwind.
- FixedBuffer: drop the unused kCapacity (clang-tidy 18 reports it as a
  dynamically initialized header static).
- ss-server on Windows: manager_addr was only read in POSIX-only code,
  which newer clang rejects (-Wunused-but-set-global); say that the
  option is ignored there.

* CI: allow running the test and build workflows by hand

Stacked PRs target their parent branch, which these workflows do not
trigger on; workflow_dispatch lets each layer run the full matrix.

* CI: let clang-tidy ignore GCC-only warning flags

The compile database comes from a GCC build, whose flags (for example
-Wno-class-memaccess) clang-tidy does not know and reports as errors.
2026-10-15 09:45:54 +08:00
..

Docker image and static Linux builds

Use ghcr.io/shadowsocks/shadowsocks-c for Linux AMD64 or ARM64. The README quick start shows server configuration, TCP/UDP port mapping and a read-only configuration mount. Docker Desktop runs these Linux images on macOS and Windows.

The default entrypoint is ss-server -c /etc/shadowsocks-c/config.json. Passing arguments overrides that default configuration argument. The image runs as UID/GID 65532:65532 unless --user is supplied; the mounted file must be readable by that user. Logs go to standard output/error. No writable filesystem is required for normal server operation.

To update a running server, pull the desired tag, remove the old container with docker rm -f shadowsocks-c, and repeat the quick-start run command. The host configuration file is preserved. latest tracks master; choose a published version tag or sha-<full-commit> to pin a build.

For a SOCKS5 client, override the entrypoint with --entrypoint /usr/local/bin/ss-local, supply a client configuration using -c, and publish the configured local TCP/UDP port. Bind the local listener to 0.0.0.0 inside the container and restrict its host mapping to loopback, such as -p 127.0.0.1:1080:1080/tcp -p 127.0.0.1:1080:1080/udp.

Publishing

.github/workflows/docker.yml builds each architecture on a native runner and exercises the actual scratch server/client containers with concurrent TCP, hostname resolution, UDP and clean shutdown. PRs run these checks without publishing. Pushes to master publish latest and sha-<full-commit>; v* version tags publish the version without the leading v and the commit tag. The multi-platform tag is assembled only after both architecture tests pass. Publishing uses the repository's GITHUB_TOKEN with packages: write.

The first image becomes available after this workflow lands on master and completes. GitHub initially creates packages as private: a package administrator must set shadowsocks-c to Public in its package settings before anonymous pulls work. This is a one-time registry setting; see GitHub's container registry documentation. Until then, the quick start also provides a local image build command.

Build locally

The Alpine/musl builder uses Clang with LLD by default and links both third-party dependencies and libc statically. WITH_STATIC=ON alone only selects static third-party libraries; this image also passes -DCMAKE_EXE_LINKER_FLAGS=-static and omits shared-library outputs. The compile, unit/vendor tests and six-method TCP/UDP relay tests run with Docker network access disabled. The final ELF check rejects interpreters and dynamic library dependencies. Initial image/toolchain installation requires networking.

From the repository root, export binaries and licenses (ARM64 example):

docker build --platform linux/arm64 -f docker/static/Dockerfile \
  --target artifacts --output type=local,dest=build-artifacts/static-linux-arm64 .

Requires Docker Buildx/BuildKit. If Docker Hub is unavailable, add --build-arg ALPINE_IMAGE=public.ecr.aws/docker/library/alpine:3.24. An alternate package mirror can be selected with --build-arg ALPINE_MIRROR=https://mirrors.aliyun.com/alpine; APK signature checks remain enabled. Docker build proxy settings apply to package installation; a broken configured proxy can be bypassed for this command with empty HTTP_PROXY, HTTPS_PROXY, ALL_PROXY build arguments and their lowercase equivalents.

Use --platform linux/amd64 and a different destination for x86-64. Docker needs a matching native builder or CPU emulation to compile and run the tests for a foreign architecture. Add --build-arg SS_MINIMAL=ON for the minimal profile.

Build the scratch runtime image using the same cached build stage:

docker build --platform linux/arm64 -f docker/static/Dockerfile \
  --target runtime -t shadowsocks-c:static-arm64 .
docker run --rm -p 8388:8388/tcp -p 8388:8388/udp \
  shadowsocks-c:static-arm64 \
  -s 0.0.0.0 -p 8388 -k example-password -m aes-256-gcm -u

The runtime image has no shell or external plugin executables. Supply any SIP003 plugin and its own runtime requirements separately. Configuration files can be bind-mounted and passed with -c. Exported binaries run on Linux with the same CPU architecture; they do not run directly on macOS or Windows.