logologo
Guide
Development
Reference
Configuration Reference
Admin Command Reference
Server Reference
Community
Security
Matrix
Guide
Development
Configuration Reference
Admin Command Reference
Server Reference
Community
Security
Matrix
logologo
Continuwuity
Configuration

Deploying

Generic
Docker
Debian
Fedora
NixOS
Arch Linux
Kubernetes
FreeBSD
TURN
Appservices
Maintenance
Troubleshooting
Development
Development Guide
Contributing
Code Style Guide
Testing
Hot Reloading
Reference
Configuration Reference
Admin Command Reference
Server Reference
Continuwuity Community Guidelines
Security Policy for Continuwuity

Last Updated: 2025/11/15 20:25:54

Previous PageGeneric
Next PageDebian

#Continuwuity for Docker

#Docker

To run Continuwuity with Docker, you can either build the image yourself or pull it from a registry.

#Use a registry

OCI images for Continuwuity are available in the registries listed below.

RegistryImageNotes
Forgejo Registryforgejo.ellis.link/continuwuation/continuwuity:latestLatest tagged image.
Forgejo Registryforgejo.ellis.link/continuwuation/continuwuity:mainMain branch image.

Use

docker image pull $LINK

to pull it to your machine.

#Run

When you have the image, you can simply run it with

docker run -d -p 8448:6167 \
    -v db:/var/lib/continuwuity/ \
    -e CONTINUWUITY_SERVER_NAME="your.server.name" \
    -e CONTINUWUITY_ALLOW_REGISTRATION=false \
    --name continuwuity $LINK

or you can use Docker Compose.

The -d flag lets the container run in detached mode. You may supply an optional continuwuity.toml config file, the example config can be found here. You can pass in different env vars to change config values on the fly. You can even configure Continuwuity completely by using env vars. For an overview of possible values, please take a look at the docker-compose.yml file.

If you just want to test Continuwuity for a short time, you can use the --rm flag, which cleans up everything related to your container after you stop it.

#Docker-compose

If the docker run command is not suitable for you or your setup, you can also use one of the provided docker-compose files.

Depending on your proxy setup, you can use one of the following files:

#For existing Traefik setup

docker-compose.for-traefik.yml
# Continuwuity - Behind Traefik Reverse Proxy

services:
  homeserver:
    ### If you already built the conduduwit image with 'docker build' or want to use the Docker Hub image,
    ### then you are ready to go.
    image: forgejo.ellis.link/continuwuation/continuwuity:latest
    restart: unless-stopped
    volumes:
      - db:/var/lib/continuwuity
      - /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
      #- ./continuwuity.toml:/etc/continuwuity.toml
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.continuwuity.rule=(Host(`matrix.example.com`) || (Host(`example.com`) && PathPrefix(`/.well-known/matrix`)))"
      - "traefik.http.routers.continuwuity.entrypoints=websecure" # your HTTPS entry point
      - "traefik.http.routers.continuwuity.tls=true"
      - "traefik.http.routers.continuwuity.service=continuwuity"
      - "traefik.http.services.continuwuity.loadbalancer.server.port=6167"
      # possibly, depending on your config:
      # - "traefik.http.routers.continuwuity.tls.certresolver=letsencrypt"
    environment:
      CONTINUWUITY_SERVER_NAME: your.server.name.example # EDIT THIS
      CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
      CONTINUWUITY_PORT: 6167 # should match the loadbalancer traefik label
      CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
      CONTINUWUITY_ALLOW_REGISTRATION: 'true'
      CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
      #CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
      CONTINUWUITY_ALLOW_FEDERATION: 'true'
      CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
      CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
      #CONTINUWUITY_LOG: warn,state_res=warn
      CONTINUWUITY_ADDRESS: 0.0.0.0
      #CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above

      # We need some way to serve the client and server .well-known json. The simplest way is via the CONTINUWUITY_WELL_KNOWN
      # variable / config option, there are multiple ways to do this, e.g. in the continuwuity.toml file, and in a separate
      # see the override file for more information about delegation
      CONTINUWUITY_WELL_KNOWN: |
        {
        client=https://your.server.name.example,
        server=your.server.name.example:443
        }
    #cpuset: "0-4" # Uncomment to limit to specific CPU cores
    ulimits: # Continuwuity uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
      nofile:
        soft: 1048567
        hard: 1048567

    ### Uncomment if you want to use your own Element-Web App.
    ### Note: You need to provide a config.json for Element and you also need a second
    ###       Domain or Subdomain for the communication between Element and Continuwuity
    ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
    # element-web:
    #     image: vectorim/element-web:latest
    #     restart: unless-stopped
    #     volumes:
    #         - ./element_config.json:/app/config.json
    #     networks:
    #         - proxy
    #     depends_on:
    #         - homeserver

volumes:
  db:

networks:
  # This is the network Traefik listens to, if your network has a different
  # name, don't forget to change it here and in the docker-compose.override.yml
  proxy:
    external: true

# vim: ts=2:sw=2:expandtab

#With Traefik included

docker-compose.with-traefik.yml
# Continuwuity - Behind Traefik Reverse Proxy

services:
  homeserver:
    ### If you already built the Continuwuity image with 'docker build' or want to use the Docker Hub image,
    ### then you are ready to go.
    image: forgejo.ellis.link/continuwuation/continuwuity:latest
    restart: unless-stopped
    volumes:
      - db:/var/lib/continuwuity
      - /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
      #- ./continuwuity.toml:/etc/continuwuity.toml
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.continuwuity.rule=(Host(`matrix.example.com`) || (Host(`example.com`) && PathPrefix(`/.well-known/matrix`)))"
      - "traefik.http.routers.continuwuity.entrypoints=websecure"
      - "traefik.http.routers.continuwuity.tls.certresolver=letsencrypt"
      - "traefik.http.services.continuwuity.loadbalancer.server.port=6167"
      # Uncomment and adjust the following if you want to use middleware
      # - "traefik.http.routers.continuwuity.middlewares=secureHeaders@file"
    environment:
      CONTINUWUITY_SERVER_NAME: your.server.name.example # EDIT THIS
      CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
      CONTINUWUITY_ALLOW_REGISTRATION: 'false' # After setting a secure registration token, you can enable this
      CONTINUWUITY_REGISTRATION_TOKEN: "" # This is a token you can use to register on the server
      #CONTINUWUITY_REGISTRATION_TOKEN_FILE: "" # Alternatively you can configure a path to a token file to read
      CONTINUWUITY_ADDRESS: 0.0.0.0
      CONTINUWUITY_PORT: 6167 # you need to match this with the traefik load balancer label if you're want to change it
      CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
      #CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
      ### Uncomment and change values as desired, note that Continuwuity has plenty of config options, so you should check out the example example config too
      # Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
      # CONTINUWUITY_LOG: info  # default is: "warn,state_res=warn"
      # CONTINUWUITY_ALLOW_ENCRYPTION: 'true'
      # CONTINUWUITY_ALLOW_FEDERATION: 'true'
      # CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
      # CONTINUWUITY_ALLOW_INCOMING_PRESENCE: true
      # CONTINUWUITY_ALLOW_OUTGOING_PRESENCE: true
      # CONTINUWUITY_ALLOW_LOCAL_PRESENCE: true
      # CONTINUWUITY_WORKERS: 10
      # CONTINUWUITY_MAX_REQUEST_SIZE: 20000000  # in bytes, ~20 MB
      # CONTINUWUITY_NEW_USER_DISPLAYNAME_SUFFIX = "🏳<200d>⚧"

      # We need some way to serve the client and server .well-known json. The simplest way is via the CONTINUWUITY_WELL_KNOWN
      # variable / config option, there are multiple ways to do this, e.g. in the continuwuity.toml file, and in a separate
      # reverse proxy, but since you do not have a reverse proxy and following this guide, this example is included
      CONTINUWUITY_WELL_KNOWN: |
        {
          client=https://your.server.name.example,
          server=your.server.name.example:443
        }
    #cpuset: "0-4" # Uncomment to limit to specific CPU cores
    ulimits: # Continuwuity uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
      nofile:
        soft: 1048567
        hard: 1048567

    ### Uncomment if you want to use your own Element-Web App.
    ### Note: You need to provide a config.json for Element and you also need a second
    ###       Domain or Subdomain for the communication between Element and Continuwuity
    ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
    # element-web:
    #     image: vectorim/element-web:latest
    #     restart: unless-stopped
    #     volumes:
    #         - ./element_config.json:/app/config.json
    #     networks:
    #         - proxy
    #     depends_on:
    #         - homeserver

  traefik:
    image: "traefik:latest"
    container_name: "traefik"
    restart: "unless-stopped"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:z"
      - "acme:/etc/traefik/acme"
      #- "./traefik_config:/etc/traefik:z"
    labels:
      - "traefik.enable=true"

      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # global redirect to https
      - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.redirs.entrypoints=web"
      - "traefik.http.routers.redirs.middlewares=redirect-to-https"

    configs:
      - source: dynamic.yml
        target: /etc/traefik/dynamic.yml

    environment:
      TRAEFIK_LOG_LEVEL: DEBUG
      TRAEFIK_ENTRYPOINTS_WEB: true
      TRAEFIK_ENTRYPOINTS_WEB_ADDRESS: ":80"
      TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO: websecure

      TRAEFIK_ENTRYPOINTS_WEBSECURE: true
      TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS: ":443"
      TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_CERTRESOLVER: letsencrypt
      #TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_MIDDLEWARES: secureHeaders@file # if you want to enabled STS

      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT: true
      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_EMAIL: # Set this to the email you want to receive certificate expiration emails for
      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_KEYTYPE: EC384
      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE: true
      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE_ENTRYPOINT: web
      TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_STORAGE: "/etc/traefik/acme/acme.json"

      TRAEFIK_PROVIDERS_DOCKER: true
      TRAEFIK_PROVIDERS_DOCKER_ENDPOINT: "unix:///var/run/docker.sock"
      TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT: false

      TRAEFIK_PROVIDERS_FILE: true
      TRAEFIK_PROVIDERS_FILE_FILENAME: "/etc/traefik/dynamic.yml"

configs:
  dynamic.yml:
    content: |
      # Optionally set STS headers, like in https://hstspreload.org
      # http:
      #   middlewares:
      #     secureHeaders:
      #       headers:
      #         forceSTSHeader: true
      #         stsIncludeSubdomains: true
      #         stsPreload: true
      #         stsSeconds: 31536000
      tls:
        options:
          default:
            cipherSuites:
              - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
              - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
              - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
              - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
              - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
              - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
            minVersion: VersionTLS12

volumes:
    db:
    acme:

networks:
    proxy:

# vim: ts=2:sw=2:expandtab

#With Caddy Docker Proxy

docker-compose.with-caddy.yml

Replace all example.com placeholders with your own domain.

services:
    caddy:
    # This compose file uses caddy-docker-proxy as the reverse proxy for Continuwuity!
    # For more info, visit https://github.com/lucaslorentz/caddy-docker-proxy
        image: lucaslorentz/caddy-docker-proxy:ci-alpine
        ports:
            - 80:80
            - 443:443
        environment:
            - CADDY_INGRESS_NETWORKS=caddy
        networks:
            - caddy
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - ./data:/data
        restart: unless-stopped
        labels:
            caddy: example.com
            caddy.0_respond: /.well-known/matrix/server {"m.server":"matrix.example.com:443"}
            caddy.1_respond: /.well-known/matrix/client {"m.server":{"base_url":"https://matrix.example.com"},"m.homeserver":{"base_url":"https://matrix.example.com"},"org.matrix.msc3575.proxy":{"url":"https://matrix.example.com"}}

    homeserver:
        ### If you already built the Continuwuity image with 'docker build' or want to use a registry image,
        ### then you are ready to go.
        image: forgejo.ellis.link/continuwuation/continuwuity:latest
        restart: unless-stopped
        volumes:
            - db:/var/lib/continuwuity
            - /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
            #- ./continuwuity.toml:/etc/continuwuity.toml
        environment:
            CONTINUWUITY_SERVER_NAME: example.com # EDIT THIS
            CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
            CONTINUWUITY_PORT: 6167
            CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
            CONTINUWUITY_ALLOW_REGISTRATION: 'true'
            CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
            #CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
            CONTINUWUITY_ALLOW_FEDERATION: 'true'
            CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
            CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
            #CONTINUWUITY_LOG: warn,state_res=warn
            CONTINUWUITY_ADDRESS: 0.0.0.0
            #CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
        networks:
            - caddy
        labels:
            caddy: matrix.example.com
            caddy.reverse_proxy: "{{upstreams 6167}}"

volumes:
    db:

networks:
    caddy:
        external: true

#For other reverse proxies

docker-compose.yml
# Continuwuity

services:
    homeserver:
        ### If you already built the Continuwuity image with 'docker build' or want to use a registry image,
        ### then you are ready to go.
        image: forgejo.ellis.link/continuwuation/continuwuity:latest
        restart: unless-stopped
        ports:
            - 8448:6167
        volumes:
            - db:/var/lib/continuwuity
            #- ./continuwuity.toml:/etc/continuwuity.toml
        environment:
            CONTINUWUITY_SERVER_NAME: your.server.name # EDIT THIS
            CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
            CONTINUWUITY_PORT: 6167
            CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
            CONTINUWUITY_ALLOW_REGISTRATION: 'true'
            CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
            #CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
            CONTINUWUITY_ALLOW_FEDERATION: 'true'
            CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
            CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
            #CONTINUWUITY_LOG: warn,state_res=warn
            CONTINUWUITY_ADDRESS: 0.0.0.0
            #CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
    #
    ### Uncomment if you want to use your own Element-Web App.
    ### Note: You need to provide a config.json for Element and you also need a second
    ###       Domain or Subdomain for the communication between Element and Continuwuity
    ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
    # element-web:
    #     image: vectorim/element-web:latest
    #     restart: unless-stopped
    #     ports:
    #         - 8009:80
    #     volumes:
    #         - ./element_config.json:/app/config.json
    #     depends_on:
    #         - homeserver

volumes:
    db:

#Override file

docker-compose.override.yml
# Continuwuity - Traefik Reverse Proxy Labels

services:
  homeserver:
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"  # Change this to the name of your Traefik docker proxy network

      - "traefik.http.routers.to-continuwuity.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)"  # Change to the address on which Continuwuity is hosted
      - "traefik.http.routers.to-continuwuity.tls=true"
      - "traefik.http.routers.to-continuwuity.tls.certresolver=letsencrypt"
      - "traefik.http.routers.to-continuwuity.middlewares=cors-headers@docker"
      - "traefik.http.services.to_continuwuity.loadbalancer.server.port=6167"

      - "traefik.http.middlewares.cors-headers.headers.accessControlAllowOriginList=*"
      - "traefik.http.middlewares.cors-headers.headers.accessControlAllowHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization"
      - "traefik.http.middlewares.cors-headers.headers.accessControlAllowMethods=GET, POST, PUT, DELETE, OPTIONS"

      # If you want to have your account on <DOMAIN>, but host Continuwuity on a subdomain,
      # you can let it only handle the well known file on that domain instead
      #- "traefik.http.routers.to-matrix-wellknown.rule=Host(`<DOMAIN>`) && PathPrefix(`/.well-known/matrix`)"
      #- "traefik.http.routers.to-matrix-wellknown.tls=true"
      #- "traefik.http.routers.to-matrix-wellknown.tls.certresolver=letsencrypt"
      #- "traefik.http.routers.to-matrix-wellknown.middlewares=cors-headers@docker"

  ### Uncomment this if you uncommented Element-Web App in the docker-compose.yml
  # element-web:
  #     labels:
  #         - "traefik.enable=true"
  #         - "traefik.docker.network=proxy"  # Change this to the name of your Traefik docker proxy network

  #         - "traefik.http.routers.to-element-web.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)"  # Change to the address on which Element-Web is hosted
  #         - "traefik.http.routers.to-element-web.tls=true"
  #         - "traefik.http.routers.to-element-web.tls.certresolver=letsencrypt"

# vim: ts=2:sw=2:expandtab

When picking the Traefik-related compose file, rename it to docker-compose.yml, and rename the override file to docker-compose.override.yml. Edit the latter with the values you want for your server.

When picking the caddy-docker-proxy compose file, it's important to first create the caddy network before spinning up the containers:

docker network create caddy

After that, you can rename it to docker-compose.yml and spin up the containers!

Additional info about deploying Continuwuity can be found here.

#Build

Official Continuwuity images are built using Docker Buildx and the Dockerfile found at docker/Dockerfile. This approach uses common Docker tooling and enables efficient multi-platform builds.

The resulting images are widely compatible with Docker and other container runtimes like Podman or containerd.

The images do not contain a shell. They contain only the Continuwuity binary, required libraries, TLS certificates, and metadata.

Click to view the Dockerfile

You can also view the Dockerfile on Forgejo.

ARG RUST_VERSION=1
ARG DEBIAN_VERSION=bookworm

FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS base
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS toolchain

# Prevent deletion of apt cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean

# Match Rustc version as close as possible
# rustc -vV
ARG LLVM_VERSION=20
# ENV RUSTUP_TOOLCHAIN=${RUST_VERSION}

# Install repo tools
# Line one: compiler tools
# Line two: curl, for downloading binaries
# Line three: for xx-verify
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install -y \
    pkg-config make jq \
    curl git software-properties-common \
    file

# LLVM packages
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    curl https://apt.llvm.org/llvm.sh > llvm.sh && \
    chmod +x llvm.sh && \
    ./llvm.sh ${LLVM_VERSION} && \
    rm llvm.sh

# Create symlinks for LLVM tools
RUN <<EOF
    set -o xtrace
    # clang
    ln -s /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang
    ln -s "/usr/bin/clang++-${LLVM_VERSION}" "/usr/bin/clang++"
    # lld
    ln -s /usr/bin/ld64.lld-${LLVM_VERSION} /usr/bin/ld64.lld
    ln -s /usr/bin/ld.lld-${LLVM_VERSION} /usr/bin/ld.lld
    ln -s /usr/bin/lld-${LLVM_VERSION} /usr/bin/lld
    ln -s /usr/bin/lld-link-${LLVM_VERSION} /usr/bin/lld-link
    ln -s /usr/bin/wasm-ld-${LLVM_VERSION} /usr/bin/wasm-ld
EOF

# Developer tool versions
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
ENV BINSTALL_VERSION=1.15.11
# renovate: datasource=github-releases depName=psastras/sbom-rs
ENV CARGO_SBOM_VERSION=0.9.1
# renovate: datasource=crate depName=lddtree
ENV LDDTREE_VERSION=0.3.7
# renovate: datasource=crate depName=timelord-cli
ENV TIMELORD_VERSION=3.0.1

# Install unpackaged tools
RUN <<EOF
    set -o xtrace
    curl --retry 5 -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
    cargo binstall --no-confirm cargo-sbom --version $CARGO_SBOM_VERSION
    cargo binstall --no-confirm lddtree --version $LDDTREE_VERSION
    cargo binstall --no-confirm timelord-cli --version $TIMELORD_VERSION
EOF

# Set up xx (cross-compilation scripts)
COPY --from=xx / /
ARG TARGETPLATFORM

# Install libraries linked by the binary
# xx-* are xx-specific meta-packages
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    xx-apt-get install -y \
    xx-c-essentials xx-cxx-essentials pkg-config \
    liburing-dev

# Set up Rust toolchain
WORKDIR /app
COPY ./rust-toolchain.toml .
RUN rustc --version \
    && xx-cargo --setup-target-triple

# Build binary
# Configure incremental compilation based on build context
ARG CARGO_INCREMENTAL=0
RUN echo "CARGO_INCREMENTAL=${CARGO_INCREMENTAL}" >> /etc/environment

# Configure pkg-config
RUN <<EOF
    set -o xtrace
    if command -v "$(xx-info)-pkg-config" >/dev/null 2>/dev/null; then
        echo "PKG_CONFIG_LIBDIR=/usr/lib/$(xx-info)/pkgconfig" >> /etc/environment
        echo "PKG_CONFIG=/usr/bin/$(xx-info)-pkg-config" >> /etc/environment
    fi
    echo "PKG_CONFIG_ALLOW_CROSS=true" >> /etc/environment
EOF

# Configure cc to use clang version
RUN <<EOF
    set -o xtrace
    echo "CC=clang" >> /etc/environment
    echo "CXX=clang++" >> /etc/environment
EOF

# Cross-language LTO
RUN <<EOF
    set -o xtrace
    echo "CFLAGS=-flto" >> /etc/environment
    echo "CXXFLAGS=-flto" >> /etc/environment
    # Linker is set to target-compatible clang by xx
    echo "RUSTFLAGS='-Clinker-plugin-lto -Clink-arg=-fuse-ld=lld'" >> /etc/environment
EOF

# Apply CPU-specific optimizations if TARGET_CPU is provided
ARG TARGET_CPU

RUN <<EOF
    set -o allexport
    set -o xtrace
    . /etc/environment
    if [ -n "${TARGET_CPU}" ]; then
        echo "CFLAGS='${CFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
        echo "CXXFLAGS='${CXXFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
        echo "RUSTFLAGS='${RUSTFLAGS} -C target-cpu=${TARGET_CPU}'" >> /etc/environment
    fi
EOF

# Prepare output directories
RUN mkdir /out

FROM toolchain AS builder


# Get source
COPY . .

# Restore timestamps from timelord cache if available
RUN --mount=type=cache,target=/timelord/ \
    echo "Restoring timestamps from timelord cache"; \
    timelord sync --source-dir /app --cache-dir /timelord;

ARG TARGETPLATFORM

# Verify environment configuration
RUN xx-cargo --print-target-triple

# Conduwuit version info
ARG GIT_COMMIT_HASH
ARG GIT_COMMIT_HASH_SHORT
ARG GIT_REMOTE_URL
ARG GIT_REMOTE_COMMIT_URL
ARG CONDUWUIT_VERSION_EXTRA
ARG CONTINUWUITY_VERSION_EXTRA
ENV GIT_COMMIT_HASH=$GIT_COMMIT_HASH
ENV GIT_COMMIT_HASH_SHORT=$GIT_COMMIT_HASH_SHORT
ENV GIT_REMOTE_URL=$GIT_REMOTE_URL
ENV GIT_REMOTE_COMMIT_URL=$GIT_REMOTE_COMMIT_URL
ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA
ENV CONTINUWUITY_VERSION_EXTRA=$CONTINUWUITY_VERSION_EXTRA

ARG RUST_PROFILE=release

# Build the binary
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git/db \
    --mount=type=cache,target=/app/target,id=continuwuity-cargo-target-${TARGET_CPU}-${TARGETPLATFORM}-${RUST_PROFILE} \
    bash <<'EOF'
    set -o allexport
    set -o xtrace
    . /etc/environment
    TARGET_DIR=($(cargo metadata --no-deps --format-version 1 | \
            jq -r ".target_directory"))
    mkdir /out/sbin
    PACKAGE=conduwuit
    xx-cargo build --locked --profile ${RUST_PROFILE} \
        -p $PACKAGE;
    BINARIES=($(cargo metadata --no-deps --format-version 1 | \
        jq -r ".packages[] | select(.name == \"$PACKAGE\") | .targets[] | select( .kind | map(. == \"bin\") | any ) | .name"))
    for BINARY in "${BINARIES[@]}"; do
        echo $BINARY
        xx-verify $TARGET_DIR/$(xx-cargo --print-target-triple)/${RUST_PROFILE}/$BINARY
        cp $TARGET_DIR/$(xx-cargo --print-target-triple)/${RUST_PROFILE}/$BINARY /out/sbin/$BINARY
    done
EOF

# Generate Software Bill of Materials (SBOM)
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git/db \
    bash <<'EOF'
    set -o xtrace
    mkdir /out/sbom
    typeset -A PACKAGES
    for BINARY in /out/sbin/*; do
        BINARY_BASE=$(basename ${BINARY})
        package=$(cargo metadata --no-deps --format-version 1 | jq -r ".packages[] | select(.targets[] | select( .kind | map(. == \"bin\") | any ) | .name == \"$BINARY_BASE\") | .name")
        if [ -z "$package" ]; then
            continue
        fi
        PACKAGES[$package]=1
    done
    for PACKAGE in $(echo ${!PACKAGES[@]}); do
        echo $PACKAGE
        cargo sbom --cargo-package $PACKAGE > /out/sbom/$PACKAGE.spdx.json
    done
EOF

# Extract dynamically linked dependencies
RUN <<'DEPS_EOF'
    set -o xtrace
    mkdir /out/libs /out/libs-root

    # Process each binary
    for BINARY in /out/sbin/*; do
        if lddtree_output=$(lddtree "$BINARY" 2>/dev/null) && [ -n "$lddtree_output" ]; then
            echo "$lddtree_output" | awk '{print $(NF-0) " " $1}' | sort -u -k 1,1 | \
                awk '{dest = ($2 ~ /^\//) ? "/out/libs-root" $2 : "/out/libs/" $2; print "install -D " $1 " " dest}' | \
                while read cmd; do eval "$cmd"; done
        fi
    done

    # Show what will be copied to runtime
    echo "=== Libraries being copied to runtime image:"
    find /out/libs* -type f 2>/dev/null | sort || echo "No libraries found"
DEPS_EOF

FROM ubuntu:latest AS prepper

# Create layer structure
RUN mkdir -p /layer1/etc/ssl/certs \
             /layer2/usr/lib \
             /layer3/sbin /layer3/sbom

# Copy SSL certs and root-path libraries to layer1 (ultra-stable)
COPY --from=base /etc/ssl/certs /layer1/etc/ssl/certs
COPY --from=builder /out/libs-root/ /layer1/

# Copy application libraries to layer2 (semi-stable)
COPY --from=builder /out/libs/ /layer2/usr/lib/

# Copy binaries and SBOM to layer3 (volatile)
COPY --from=builder /out/sbin/ /layer3/sbin/
COPY --from=builder /out/sbom/ /layer3/sbom/

# Fix permissions after copying
RUN chmod -R 755 /layer1 /layer2 /layer3

FROM scratch

WORKDIR /

# Copy ultra-stable layer (SSL certs, system libraries)
COPY --from=prepper /layer1/ /

# Copy semi-stable layer (application libraries)
COPY --from=prepper /layer2/ /

# Copy volatile layer (binaries, SBOM)
COPY --from=prepper /layer3/ /

# Inform linker where to find libraries
ENV LD_LIBRARY_PATH=/usr/lib

# Continuwuity default port
EXPOSE 8008

CMD ["/sbin/conduwuit"]

To build an image locally using Docker Buildx, you can typically run a command like:

# Build for the current platform and load into the local Docker daemon
docker buildx build --load --tag continuwuity:latest -f docker/Dockerfile .

# Example: Build for specific platforms and push to a registry.
# docker buildx build --platform linux/amd64,linux/arm64 --tag registry.io/org/continuwuity:latest -f docker/Dockerfile . --push

# Example: Build binary optimized for the current CPU
# docker buildx build --load --tag continuwuity:latest --build-arg TARGET_CPU=native -f docker/Dockerfile .

Refer to the Docker Buildx documentation for more advanced build options.

#Run

If you have already built the image or want to use one from the registries, you can start the container and everything else in the compose file in detached mode with:

docker compose up -d

Note: Don't forget to modify and adjust the compose file to your needs.

#Use Traefik as Proxy

As a container user, you probably know about Traefik. It is an easy-to-use reverse proxy for making containerized apps and services available through the web. With the Traefik-related docker-compose files provided above, it is equally easy to deploy and use Continuwuity, with a small caveat. If you have already looked at the files, you should have seen the well-known service, which is the small caveat. Traefik is simply a proxy and load balancer and cannot serve any kind of content. For Continuwuity to federate, we need to either expose ports 443 and 8448 or serve two endpoints: .well-known/matrix/client and .well-known/matrix/server.

With the service well-known, we use a single nginx container that serves those two files.

Alternatively, you can use Continuwuity's built-in delegation file capability. Set up the delegation files in the configuration file, and then proxy paths under /.well-known/matrix to continuwuity. For example, the label traefik.http.routers.continuwuity.rule=(Host(`matrix.ellis.link`) || (Host(`ellis.link`) && PathPrefix(`/.well-known/matrix`))) does this for the domain ellis.link.

#Voice communication

See the TURN page.