├── ecs ├── .gitignore ├── conf │ ├── inetrc │ ├── dhparams.pem │ ├── server.pem │ ├── ejabberdctl.cfg │ └── ejabberd.yml ├── bin │ ├── erl │ └── ejabberdctl ├── config.exs ├── vars.config ├── rel │ ├── dev.exs │ ├── prod.exs │ └── config.exs ├── build.sh ├── hooks │ └── build ├── HUB-README.md ├── Dockerfile ├── ejabberd-docker-install.bat └── README.md ├── CHANGELOG.md ├── mix ├── Dockerfile └── README.md ├── code-server └── Dockerfile ├── .github └── workflows │ ├── code-server.yml │ ├── devcontainer.yml │ └── tests.yml ├── devcontainer └── Dockerfile ├── README.md ├── CODE_OF_CONDUCT.md └── CONTRIBUTING.md /ecs/.gitignore: -------------------------------------------------------------------------------- 1 | db/ 2 | ejbuild/ 3 | log/ 4 | *.tar.gz 5 | 6 | -------------------------------------------------------------------------------- /ecs/conf/inetrc: -------------------------------------------------------------------------------- 1 | {lookup,["file","native"]}. 2 | {host,{127,0,0,1}, ["localhost","@@HOSTNAME@@"]}. 3 | {file, resolv, "/etc/resolv.conf"}. 4 | -------------------------------------------------------------------------------- /ecs/bin/erl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | ROOTDIR=/home/ejabberd 5 | BINDIR=$ROOTDIR/bin 6 | EMU=beam 7 | PROGNAME=`echo $0 | sed 's/.*\\///'` 8 | export EMU 9 | export ROOTDIR 10 | export BINDIR 11 | export PROGNAME 12 | exec "$BINDIR/erlexec" ${1+"$@"} 13 | -------------------------------------------------------------------------------- /ecs/config.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # This is standard path in the context of ejabberd release 4 | config :ejabberd, 5 | file: "config/ejabberd.yml", 6 | log_path: 'log/ejabberd.log' 7 | 8 | # Customize Mnesia directory: 9 | config :mnesia, 10 | dir: 'database/' 11 | -------------------------------------------------------------------------------- /ecs/vars.config: -------------------------------------------------------------------------------- 1 | {tools, false}. 2 | 3 | {mysql, true}. 4 | {odbc, true}. 5 | {mssql, false}. 6 | {pgsql, true}. 7 | {sqlite, true}. 8 | {redis, true}. 9 | {pam, false}. 10 | {zlib, true}. 11 | {elixir, true}. 12 | {stun, true}. 13 | {sip, true}. 14 | {lua, true}. 15 | 16 | {release_dir, "${SCRIPT_DIR%/*}"}. 17 | -------------------------------------------------------------------------------- /ecs/rel/dev.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # This is standard path in the context of ejabberd release 4 | config :ejabberd, 5 | file: "/home/ejabberd/conf/ejabberd.yml", 6 | log_path: '/home/ejabberd/logs/ejabberd.log' 7 | 8 | # Customize Mnesia directory: 9 | config :mnesia, 10 | dir: '/home/ejabberd/database/' 11 | -------------------------------------------------------------------------------- /ecs/rel/prod.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # This is standard path in the context of ejabberd release 4 | config :ejabberd, 5 | file: "/home/ejabberd/conf/ejabberd.yml", 6 | log_path: '/home/ejabberd/logs/ejabberd.log' 7 | 8 | # Customize Mnesia directory: 9 | config :mnesia, 10 | dir: '/home/ejabberd/database/' 11 | -------------------------------------------------------------------------------- /ecs/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | current=$(date +%y.%m) 4 | version=${1:-$current} 5 | 6 | docker build \ 7 | --build-arg VERSION=$version \ 8 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 9 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 10 | -t ejabberd/ecs:$version . 11 | 12 | [ "$version" = "latest" ] || docker tag ejabberd/ecs:$version ejabberd/ecs:latest 13 | -------------------------------------------------------------------------------- /ecs/conf/dhparams.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIIBCAKCAQEA6/sznqAfpkS3X0jBBmYgIm3XAq2mteWVGD3/CbQ2rfk8/38fEGN7 3 | j5G+WnulMkf9WvGtRnFQrsIsnHUyMy4ay1p7ERKCA+TLd+biOIFXOnABpvnVcPjQ 4 | LP/G5t7kJ/kM5G8eHDk1R7rvhA8Xecv7oX6QFqsdYRSM1tO9HR0l53tSPQ0swk6v 5 | PhWZmOSfjugfOKwHMuXwI112pyXI1XEqC/mq+CJNJw0Z3eLcOcJWuFIZuoOwXDdU 6 | GXK0kEypq+bMTVuq1B3WMxFon8PffQ/rmbH6Bk3SXrJz6koKCcBhm019/fuNSGQ5 7 | 6uVCmIbxWeYL6Yn8qRBVMlgC3DEGMzONWwIBAg== 8 | -----END DH PARAMETERS----- 9 | -------------------------------------------------------------------------------- /ecs/hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # See documentation for details: 4 | # https://docs.docker.com/docker-hub/builds/advanced/ 5 | 6 | # Use $VERSION build environment variable or the Docker repository tag being built 7 | version=${VERSION:-${DOCKER_TAG}} 8 | 9 | docker build \ 10 | --build-arg VERSION=$version \ 11 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 12 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 13 | -f $DOCKERFILE_PATH \ 14 | -t $IMAGE_NAME . 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 19.08 2 | 3 | - Update default configuration 4 | - Add sqlite libs 5 | - Fix sample Let's Encrypt configuration 6 | - Add badges and links docker README 7 | - Allow mapping of upload dir as volume 8 | - Fix INET_DIST_INTERFACE 9 | - Add travis support 10 | - Add STUN/TURN & SIP support 11 | - Add libgd for image manupilation 12 | - Add Dockerhub build hook 13 | - Keep SQL init scripts in database volume 14 | - Update use of new distillery 15 | 16 | # Version 19.05 17 | 18 | - Update default configuration 19 | -------------------------------------------------------------------------------- /mix/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.22 2 | LABEL maintainer="ProcessOne " \ 3 | product="Ejabberd mix development environment" 4 | 5 | # Install required dependencies 6 | RUN apk upgrade --update musl \ 7 | && apk add build-base git zlib-dev openssl-dev yaml-dev expat-dev sqlite-dev \ 8 | gd-dev jpeg-dev libpng-dev libwebp-dev autoconf automake bash \ 9 | elixir erlang-reltool erlang-odbc file curl \ 10 | && rm -rf /var/cache/apk/* 11 | 12 | # Setup runtime environment 13 | RUN mix local.hex --force \ 14 | && mix local.rebar --force 15 | 16 | ENTRYPOINT ["/usr/bin/mix"] 17 | CMD ["compile"] 18 | -------------------------------------------------------------------------------- /code-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:sid-slim 2 | 3 | RUN apt-get update \ 4 | && apt-get -y --no-install-recommends install \ 5 | curl ca-certificates \ 6 | autoconf automake git make gcc g++ \ 7 | erlang erlang-dev elixir rebar3 \ 8 | libexpat1-dev libgd-dev libpam0g-dev \ 9 | libsqlite3-dev libwebp-dev libyaml-dev \ 10 | libssl-dev 11 | 12 | RUN curl -fsSL https://code-server.dev/install.sh | sh 13 | 14 | RUN addgroup vscode --gid 1000 \ 15 | && adduser --shell /bin/bash --ingroup vscode vscode -u 1000 16 | 17 | USER vscode 18 | 19 | RUN /usr/bin/code-server --install-extension erlang-ls.erlang-ls 20 | 21 | WORKDIR /home/vscode 22 | RUN echo "export PATH=/workspaces/ejabberd/_build/relive:$PATH" >>.bashrc \ 23 | && echo "COOKIE" >.erlang.cookie \ 24 | && chmod 400 .erlang.cookie 25 | 26 | WORKDIR /workspaces/ejabberd 27 | VOLUME ["workspaces/ejabberd"] 28 | EXPOSE 1870 1883 4369-4399 5210 5222 5269 5280 5443 29 | 30 | ENTRYPOINT ["code-server", "--bind-addr", "0.0.0.0:1870", "--auth", "none", "/workspaces/ejabberd"] 31 | -------------------------------------------------------------------------------- /.github/workflows/code-server.yml: -------------------------------------------------------------------------------- 1 | name: code-server 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/code-server.yml' 7 | - 'code-server/**' 8 | 9 | jobs: 10 | 11 | code-server: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | 20 | - uses: actions/checkout@v4 21 | 22 | - name: Build image 23 | run: docker build 24 | code-server 25 | --tag code-server 26 | 27 | - name: Login to GitHub Container Registry 28 | uses: docker/login-action@v3 29 | with: 30 | registry: ghcr.io 31 | username: ${{ github.repository_owner }} 32 | password: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | - name: Push image 35 | run: | 36 | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/code-server 37 | # Strip git ref prefix from version 38 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 39 | # Use Docker `latest` tag convention 40 | [ "$VERSION" == "master" ] && VERSION=latest 41 | docker tag code-server $IMAGE_ID:$VERSION 42 | docker push $IMAGE_ID:$VERSION 43 | -------------------------------------------------------------------------------- /.github/workflows/devcontainer.yml: -------------------------------------------------------------------------------- 1 | name: devcontainer 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/devcontainer.yml' 7 | - 'devcontainer/**' 8 | 9 | jobs: 10 | 11 | devcontainer: 12 | 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | 20 | - uses: actions/checkout@v4 21 | 22 | - name: Build image 23 | run: docker build 24 | devcontainer 25 | --tag devcontainer 26 | 27 | - name: Login to GitHub Container Registry 28 | uses: docker/login-action@v3 29 | with: 30 | registry: ghcr.io 31 | username: ${{ github.repository_owner }} 32 | password: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | - name: Push image 35 | run: | 36 | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/devcontainer 37 | # Strip git ref prefix from version 38 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 39 | # Use Docker `latest` tag convention 40 | [ "$VERSION" == "master" ] && VERSION=latest 41 | docker tag devcontainer $IMAGE_ID:$VERSION 42 | docker push $IMAGE_ID:$VERSION 43 | -------------------------------------------------------------------------------- /ecs/rel/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Releases.Config, 2 | # This sets the default release built by `mix release` 3 | default_release: :default, 4 | # This sets the default environment used by `mix release` 5 | default_environment: :dev 6 | 7 | # For a full list of config options for both releases 8 | # and environments, visit https://hexdocs.pm/distillery/configuration.html 9 | 10 | 11 | # You may define one or more environments in this file, 12 | # an environment's settings will override those of a release 13 | # when building in that environment, this combination of release 14 | # and environment configuration is called a profile 15 | 16 | environment :dev do 17 | set dev_mode: true 18 | set include_erts: false 19 | set config: "rel/dev.exs" 20 | set cookie: :"!Vcuwp?y@d{7=`5Ha*2*PLw:i8;i:9B|tq75|K]kt?T]_nap/or,7xBylYJ!N;m{" 21 | end 22 | 23 | environment :prod do 24 | set include_erts: true 25 | set include_src: false 26 | set config: "rel/prod.exs" 27 | set cookie: :"HmewW_sUao={>LXTD8,g;xBu`.i]tq7Dz.m2?ZqO :ejabberd_auth.try_register("test", "localhost", "passw0rd") 50 | {:atomic, :ok} 51 | ``` 52 | 53 | You can then connect with user test@localhost (password: passw0rd) on server on localhost port 5222 and use those parameters to connect with an XMPP client. 54 | 55 | ## Get into the container 56 | 57 | If you want to run Erlang command line, you can do so by opening a shell inside the container: 58 | 59 | ```bash 60 | docker run -it -v $(pwd):$(pwd) -w $(pwd) --entrypoint="/bin/sh" ejabberd/mix 61 | ``` 62 | 63 | ## Getting Elixir version 64 | 65 | ```bash 66 | docker run -it --rm -v $(pwd):$(pwd) -w $(pwd) ejabberd/mix --version 67 | Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:2:2] [async-threads:10] [kernel-poll:false] 68 | 69 | Mix 1.3.4 70 | ``` 71 | 72 | ## Build the image 73 | 74 | Building the image is not needed if you simply want to use it. You can simply use the one from [ejabberd Docker Hub](https://hub.docker.com/u/ejabberd/dashboard/). 75 | 76 | ```bash 77 | docker build -t ejabberd/mix . 78 | ``` 79 | 80 | ## Troubleshooting 81 | 82 | ### Clock resync 83 | 84 | If you have warning about file timestamp being out of sync (Like 'Clock skew detected'), you may want to force resync your clock before running the build. Docker on MacOS does not force clock resync of Docker after the laptop went to sleep. 85 | 86 | You can force clock resync as follow: 87 | 88 | ```bash 89 | docker run -it --rm --privileged --entrypoint="/sbin/hwclock" ejabberd/mix -s 90 | ``` 91 | 92 | You can check if the clock of your laptop is in sync with the one inside Docker with the following command: 93 | 94 | ```bash 95 | docker run --rm --entrypoint="/bin/sh" ejabberd/mix -c date -u && date -u 96 | ``` 97 | 98 | -------------------------------------------------------------------------------- /ecs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/golang:1.25-alpine AS api 2 | RUN go install -v \ 3 | github.com/processone/ejabberd-api/cmd/ejabberd@latest \ 4 | && mv bin/ejabberd bin/ejabberdapi 5 | 6 | FROM docker.io/ejabberd/mix AS builder 7 | ARG VERSION 8 | ENV VERSION=${VERSION:-latest} \ 9 | MIX_ENV=prod 10 | LABEL maintainer="ProcessOne " \ 11 | product="Ejabberd Community Server builder" 12 | 13 | # Get ejabberd sources, dependencies, configuration 14 | RUN git clone https://github.com/processone/ejabberd.git 15 | WORKDIR /ejabberd 16 | COPY vars.config . 17 | COPY config.exs config/ 18 | COPY rel/*exs rel/ 19 | RUN git checkout ${VERSION/latest/HEAD} \ 20 | \ 21 | && if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then \ 22 | echo '{vsn, "'"$VERSION.0"'"}.' >> vars.config; \ 23 | else \ 24 | echo '{vsn, "0.0.0"}.' >> vars.config; \ 25 | fi \ 26 | \ 27 | && mix deps.get \ 28 | && (cd deps/eimp; ./configure) 29 | 30 | # Compile 31 | RUN MIX_ENV=prod mix release 32 | 33 | # Prepare runtime environment 34 | RUN mkdir runtime \ 35 | && tar -C runtime -zxf _build/prod/ejabberd-*.tar.gz \ 36 | && cd runtime \ 37 | && cp releases/*/start.boot bin \ 38 | && cp releases/*/start_clean.boot bin \ 39 | && echo 'beam_lib:strip_files(filelib:wildcard("lib/*/ebin/*beam")), init:stop().' | erl >/dev/null \ 40 | && mv erts*/bin/* bin \ 41 | && EJABBERD_VERSION=`(cd releases; ls -1 -d *.*.*)` \ 42 | && rm -rf releases erts* bin/*src bin/dialyzer bin/typer etc \ 43 | && rm bin/ejabberd \ 44 | && mkdir lib/ejabberd-$EJABBERD_VERSION/priv/bin \ 45 | && cp /usr/lib/elixir/bin/* bin/ \ 46 | && sed -i 's|ERL_EXEC="erl"|ERL_EXEC="/home/ejabberd/bin/erl"|' bin/elixir \ 47 | && cp /ejabberd/tools/captcha*sh bin/ \ 48 | && cp -r /ejabberd/sql lib/ejabberd-*/priv 49 | 50 | # Runtime container 51 | FROM docker.io/alpine:3.22 52 | ARG VERSION 53 | ARG VCS_REF 54 | ARG BUILD_DATE 55 | ENV TERM=xterm \ 56 | LC_ALL=C.UTF-8 \ 57 | LANG=en_US.UTF-8 \ 58 | LANGUAGE=en_US.UTF-8 \ 59 | REPLACE_OS_VARS=true \ 60 | HOME=/home/ejabberd \ 61 | PATH="$PATH:/home/ejabberd/bin" \ 62 | VERSION=${VERSION:-latest} 63 | LABEL maintainer="ProcessOne " \ 64 | product="Ejabberd Community Server Official Docker Image" \ 65 | version=$VERSION \ 66 | org.label-schema.vcs-ref=$VCS_REF \ 67 | org.label-schema.vcs-url="https://github.com/processone/docker-ejabberd" \ 68 | org.label-schema.build-date=$BUILD_DATE \ 69 | org.label-schema.name="Ejabberd Community Server Official Docker Image" \ 70 | org.label-schema.description="Robust, Scalable and Extensible Realtime Server using XMPP, MQTT and SIP" \ 71 | org.label-schema.url="https://www.ejabberd.im/" \ 72 | org.label-schema.vendor="ProcessOne" \ 73 | org.label-schema.version=$VERSION \ 74 | org.label-schema.schema-version="1.0" 75 | 76 | # Create directory structure and user for ejabberd 77 | RUN addgroup ejabberd -g 9000 \ 78 | && adduser -s /bin/sh -D -G ejabberd ejabberd -u 9000 \ 79 | && mkdir -p /home/ejabberd/conf /home/ejabberd/database /home/ejabberd/logs /home/ejabberd/upload \ 80 | && chown -R ejabberd:ejabberd /home/ejabberd \ 81 | && ln -fs /home/ejabberd /opt/ejabberd \ 82 | && ln -fs /home/ejabberd /opt/ejabberd-$VERSION 83 | 84 | # Install required dependencies 85 | RUN apk upgrade --update-cache --no-progress \ 86 | && apk add \ 87 | expat \ 88 | freetds \ 89 | gd \ 90 | jpeg \ 91 | libgd \ 92 | libpng \ 93 | libstdc++ \ 94 | libwebp \ 95 | ncurses-libs \ 96 | openssl \ 97 | sqlite \ 98 | sqlite-libs \ 99 | tini \ 100 | unixodbc \ 101 | yaml \ 102 | zlib \ 103 | && ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so \ 104 | && rm -rf /var/cache/apk/* 105 | 106 | # Install ejabberd 107 | WORKDIR $HOME 108 | COPY --from=builder /ejabberd/runtime . 109 | COPY bin/* bin/ 110 | COPY --from=api /go/bin/ejabberdapi bin/ejabberdapi 111 | RUN chmod 755 bin/ejabberdctl bin/ejabberdapi bin/erl bin/captcha*.sh \ 112 | && mkdir -p /home/ejabberd/sql \ 113 | && cp /home/ejabberd/lib/ejabberd-*/priv/sql/* /home/ejabberd/database/ \ 114 | && cp /home/ejabberd/lib/ejabberd-*/priv/sql/* /home/ejabberd/sql/ 115 | COPY --chown=ejabberd:ejabberd conf conf/ 116 | ADD --chown=ejabberd:ejabberd https://download.process-one.net/cacert.pem conf/cacert.pem 117 | 118 | # Setup runtime environment 119 | USER ejabberd 120 | VOLUME ["$HOME/database","$HOME/conf","$HOME/logs","$HOME/upload"] 121 | EXPOSE 1880 1883 4369-4399 5210 5222 5269 5280 5443 5478 7777 50000-50099 122 | 123 | ENTRYPOINT ["/sbin/tini","--","/home/ejabberd/bin/ejabberdctl"] 124 | CMD ["foreground"] 125 | -------------------------------------------------------------------------------- /ecs/conf/server.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDkzs+QWstSzyRN 3 | WaWCYbxyuKzoRfGpxDi54YOLoyISV4ulMVuYn3Uz6AJGH1d4oqh/JAaVOx1h/bdB 4 | nhAYy25c0P7dYtqdco3b1SMZIt3EYwHsgedTVNqZjG1+ExjqUiU8V8r7tGLFBjO/ 5 | O7H9z0zXbJ7IYhVdn99ijro8WHdEqNpnWHxVQF/KoVeTvFfcopD9QBhcu1uvwiMU 6 | G4mCOpYbysl/9NQCsNP4TV9pQ/6D6jQjIGTzuXO74cEhtibaLl7e1aPDIyqzicwS 7 | SgneKM/ypG8X9VgM81r3/nfe5us/vrIW0uXvH3qOu+/b76HULdaZ8OAItazzBoUO 8 | tl+/kakCN5J8uoKdP61hzPZWCLT84T79WQpWOa4dDiQPtuOMMlp0KZDDH+fW3WMG 9 | KmF8xiFigmPmb9SYyAVsZH9OsQeglRSS1dEV2vVzH3WpVTrZdPSM6sOhZel9fOXI 10 | kfrlZXlFvQHltvi0jUeeYM8z1e3iFAzJ6IXIKu2Ubbto6khFCZTx4FAJohOoWRJg 11 | ZcMmnGHlJNkRBYH5ImQupLhwSr2XEPPhidW5u87N+ZNk9np/ZykCcI1xiXFfgcnr 12 | CgmEWjXUV8nhgiXyl5EFAqyOlrsjc1LLL3ykzNZXHyQSFjaZe5HCDx2NjxRsdlKf 13 | gcLuifVvaaM5Vmb4GyHLr3R2HNTchwIDAQABAoICABSYVPCOa5zBjmfz+MTanUbK 14 | Pq4NlaoNQnngxaFtBqUWAjjVT4RWXgtVB9dd/Nab9YS0Lm+CSavHVZZvbs18iiuK 15 | mFY/DqR7ai9uaOFbVRDXRJYQP00t5xtJG5XsjCKTUEy1Lk0nuHg5jbDZ47SP0z/U 16 | xBoPUhMCT+4kILlL0Th55mbE/TQDPlwT17kxi8sNSvWwrkCykJcqZ1PpAqCZO2e3 17 | AholZGv1lK7V8A+ekis3iofa45tYhwVTVbn+FlMvN1ThcAMhiDdu4NKInYjErasO 18 | h/1lFQ7dXMPTwOBscqJjPM53H0E92R895WIrmKprkdR7bP77s1yQqqtfWRD6QHcK 19 | CK3dZ4GRYnL/2nsoFEi2UbKkBy+gy8z7fclv5NUfZG1mjPFMgsVC2m+6xNS1Owld 20 | YFx8pxrudxEoKOzDYd5SBDfpGkda3+1IqsPBme+Mqu4Q1fluQLBBP/oIt+RIqHMu 21 | J3ZZYPJVKUBJHKKGqnYeIsh2Ju/8IrT9dhWWRzfzhh0+hMHJjW7sXrbVm8OYpge0 22 | WBGkDf5zoBL2Kpbf6NIk68JTh+jpDYOvZbYjnPHji8zpDaxFaOgpX/WLaOVxMm10 23 | pw1kAgRhDeV87gjVdW+4NOhWfRjW/m71YAZ3NNTYk7x0KlwNNI//MvwdvHX969Td 24 | T6kUmW9Kg2ezolSs/aphAoIBAQD5Ya6lcuQuBLTBQIS7+eMM1N0j6QhBvd5Tzyr+ 25 | 6oXND3vA08skDd2Zi7m8JiPUn7ywcu+yo+H+MJAqKqPMARWSJQQ/CYQ1N+i/uZN7 26 | 1xqnTWubNtxJtikbRnCWCmVNzCSl+H9Z41eWygrO+V4c6HT9Zx1X6VOSArPlJkWw 27 | fCHzuJ3N5W8bR7RmOuVHZ/3/ZD1APkkOnqBmUZREBfiQNbGmYK3NG1Xa49MRGM3f 28 | 7YRTx3A3sucj9gKV/N1YyaLUcTAy0OUtqqcSBsUTfBPU/ztY0yg3+Xdn5WYRM1ZL 29 | Pim8OMtEP3ZdyKAb07RQr3OM04gximWesxiG74rYVVMyk0u3AoIBAQDq4VliNJya 30 | 7Gb9UyzjCiLfovpVbNSheP/KDSk2mBMwJpzMczMzhuvfIReFQFxefORALpgAmpPq 31 | bD79cOEKfknrseSLfPMGKB38RCkaBXrnaPQZ3aaXktMlrk2rOVIcawrYK3He6j0l 32 | 5fh+6uXws0jeifXzzMAmDvpkFRIGVjeJYZFdXQHMZW2ZdZFpJZj+eiWkTMDtv0L3 33 | Pl2GQVRWma/J4SH5pcHg5kLdTv8CoLRnXkDQt/BgT1Vblj8P92oArLN1zKzd/Vbu 34 | l7kFICwkn91FC0PdzWpoJAWtKlQbX19TWZ0YQLkVPB+DSjiJn+zu5+P1STKoy4eS 35 | V+mnVwS9dJWxAoIBAQDRI0NkywpjMV0pKGNohXMNQsLGJ9rdz05ReM61A5b8VrzR 36 | BI07CknKSCyWhmHMIb4LOAifBuMy2iU8x4+gOVSOwG5kMyXlv6J4jzgJRma64amZ 37 | fJOXWaX1ksg+fC9i5A3kBpc+mfLNqVkRJH3xvlzGWqB4JlwIei3Fwaf2DtQmYbAt 38 | iPj5ptO6XDAGB0cy1Cyt4M5B7XPU+xtdRKikv/Lg9fkrpn4E2MUcdHHLFpS/8eTB 39 | QU6okNqwUGSM3mSoxtfF9fwMpS4BZx4Fbivvl7B8mHEFKDgkMHS9fhEjftlfWT3W 40 | WWZ51hO34n44oDtGYeu4qDj6s3WhS+hj7oKcSnzXAoIBAQChywkfNM6LiTyIU7sf 41 | yeOr2UBHh+gQMqFANUCmcqsFCBQKYXBrlgz0bn8dMvBtxeoUrAPYpqItYNMjEZZy 42 | +/s/eBLOYwukug2tr+YAHrZCoXFEQAJ5dpIr6p1lzN5a5QFbVtHhk8j7SiaahoRO 43 | AbG4WaAqiGfzz217c2gvJUSsRaE0htSuT0/n3ayEmXmCEfHL83MSUiV22mbiDC5k 44 | WQlWLtaQeTXMDdERUVEoJjse2TYckBfsv7k0lZJrFSrXvB8CjjKl19UCFVFFzpuZ 45 | vuVUoiXlq5IYmneuiPHWySOI7rnf8vWLQxcSARZg9uDm0KWeWQWkcE666APzWWXS 46 | e3SBAoIBAQCtRv8ruFBDuv99l4WKn0ZEODdY+Uxbz1rREzVJx7hEzkif5viJl9Hi 47 | AZnqJPd8I0MMEvplDhe2X6TnF1jjxx+z4U72boY8IPY0rw+kYzBqMwywkCzb+Jn2 48 | qmBtX1XXRsSO1U7pW/ej6RyLQqtMt+lKd5vVxu7fOo3OYmcEJxD4AylUNU7cCFwP 49 | ZtFxe+uddzWYM3O55OrjHEj1LIcJb5n/46Owd2OUXMRNxQ3lv0fBKDWN/1bXiNEh 50 | efb02fSq5nIpoxmHMG/s+PEHB1uDqUjNkNZK42DyrMkwQwnN9M8ZetRWhqu5QgxX 51 | LQheTRhu+UYYcbxy+AHQ8pqBBdnRsxLZ 52 | -----END PRIVATE KEY----- 53 | -----BEGIN CERTIFICATE----- 54 | MIIFnDCCA4SgAwIBAgIJANCgN0JN5H4RMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV 55 | BAYTAkZSMQ4wDAYDVQQIDAVQYXJpczEOMAwGA1UEBwwFUGFyaXMxEzARBgNVBAoM 56 | ClByb2Nlc3NPbmUxCzAJBgNVBAsMAklUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcN 57 | MTcxMjI5MTExMzA3WhcNNDUwNTE2MTExMzA3WjBjMQswCQYDVQQGEwJGUjEOMAwG 58 | A1UECAwFUGFyaXMxDjAMBgNVBAcMBVBhcmlzMRMwEQYDVQQKDApQcm9jZXNzT25l 59 | MQswCQYDVQQLDAJJVDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B 60 | AQEFAAOCAg8AMIICCgKCAgEA5M7PkFrLUs8kTVmlgmG8cris6EXxqcQ4ueGDi6Mi 61 | EleLpTFbmJ91M+gCRh9XeKKofyQGlTsdYf23QZ4QGMtuXND+3WLanXKN29UjGSLd 62 | xGMB7IHnU1TamYxtfhMY6lIlPFfK+7RixQYzvzux/c9M12yeyGIVXZ/fYo66PFh3 63 | RKjaZ1h8VUBfyqFXk7xX3KKQ/UAYXLtbr8IjFBuJgjqWG8rJf/TUArDT+E1faUP+ 64 | g+o0IyBk87lzu+HBIbYm2i5e3tWjwyMqs4nMEkoJ3ijP8qRvF/VYDPNa9/533ubr 65 | P76yFtLl7x96jrvv2++h1C3WmfDgCLWs8waFDrZfv5GpAjeSfLqCnT+tYcz2Vgi0 66 | /OE+/VkKVjmuHQ4kD7bjjDJadCmQwx/n1t1jBiphfMYhYoJj5m/UmMgFbGR/TrEH 67 | oJUUktXRFdr1cx91qVU62XT0jOrDoWXpfXzlyJH65WV5Rb0B5bb4tI1HnmDPM9Xt 68 | 4hQMyeiFyCrtlG27aOpIRQmU8eBQCaITqFkSYGXDJpxh5STZEQWB+SJkLqS4cEq9 69 | lxDz4YnVubvOzfmTZPZ6f2cpAnCNcYlxX4HJ6woJhFo11FfJ4YIl8peRBQKsjpa7 70 | I3NSyy98pMzWVx8kEhY2mXuRwg8djY8UbHZSn4HC7on1b2mjOVZm+Bshy690dhzU 71 | 3IcCAwEAAaNTMFEwHQYDVR0OBBYEFCTwPSToCR8fTNGz5o2Czlc/BlfqMB8GA1Ud 72 | IwQYMBaAFCTwPSToCR8fTNGz5o2Czlc/BlfqMA8GA1UdEwEB/wQFMAMBAf8wDQYJ 73 | KoZIhvcNAQELBQADggIBADnvcqMt+ESTM12BuX6E+4mA0iR//Lxp8FzQkZEb7YPR 74 | eIor6iPVDX+oRo/uVhEPvjH+SXGb+9FOOexIoxoB4C+1UpBRHBS2PROX0BNmIDb7 75 | abRY6iCkIlyrVkVAtswNZdJHLJX5OMPfV52p6HELd4oYlI+AnQDXkrHA8DD7Y59r 76 | o5MonTJV26L2mq2+ceciqviZzQLA0QJxTOPLaz/pq1a2bauP0Q1EjhB0nYJ9hZxp 77 | CuAWXrVA7veElXg7EKyrQrOt6kcnNqpBj7Pk3R1Zq2xNMlfMMqQlJFt7uKy49EwI 78 | qGgmVj1ScL/Savp967LjRaTz0CnJhSLUlCmLq/4JGPWI/AKEi0Hg14EgRiGbha2B 79 | E+3sQxWmCWDeIMLM6m3001VMyT1JgEoJkbhGruDayxPDQuG4nrpK8lNMrnS1Ji0j 80 | 6VQ6OL66RFlpK+/LRYc8cXsh6+hDUaXLQTPs9v880SpoerbjW5Rh9Z7Dh+fX2SJj 81 | 8x6IbNbppn7SGD4iOaHd6+gq0j6iB1zOcTaU5OnZGb4qWNyqm0m94UWFMMr5zlmn 82 | /MwQAnvgLhfcQ1T5iq3AAIrC1D0jn0b5D2CdCFNW1DmbDZiy8db+Pz5g36G9OceQ 83 | oFdxG1n7Nre84DwOlohVYtFkeYy3D6hd+/sbyNa5YqswuKLdm6fWrp/e3es1auXy 84 | -----END CERTIFICATE----- 85 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We'd love for you to contribute to our source code and to make our project even better than it is 4 | today! Here are the guidelines we'd like you to follow: 5 | 6 | * [Code of Conduct](#coc) 7 | * [Questions and Problems](#question) 8 | * [Issues and Bugs](#issue) 9 | * [Feature Requests](#feature) 10 | * [Issue Submission Guidelines](#submit) 11 | * [Pull Request Submission Guidelines](#submit-pr) 12 | * [Signing the CLA](#cla) 13 | 14 | ## Code of Conduct 15 | 16 | Help us keep our community open-minded and inclusive. Please read and follow our [Code of Conduct][coc]. 17 | 18 | ## Questions, Bugs, Features 19 | 20 | ### Got a Question or Problem? 21 | 22 | Do not open issues for general support questions as we want to keep GitHub issues for bug reports 23 | and feature requests. You've got much better chances of getting your question answered on dedicated 24 | support platforms, the best being [Stack Overflow][stackoverflow]. 25 | 26 | Stack Overflow is a much better place to ask questions since: 27 | 28 | - there are thousands of people willing to help on Stack Overflow 29 | - questions and answers stay available for public viewing so your question / answer might help 30 | someone else 31 | - Stack Overflow's voting system assures that the best answers are prominently visible. 32 | 33 | To save your and our time, we will systematically close all issues that are requests for general 34 | support and redirect people to the section you are reading right now. 35 | 36 | ### Found an Issue or Bug? 37 | 38 | If you find a bug in the source code, you can help us by submitting an issue to our 39 | [GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. 40 | 41 | ### Missing a Feature? 42 | 43 | You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. 44 | 45 | If you would like to implement a new feature then consider what kind of change it is: 46 | 47 | * **Major Changes** that you wish to contribute to the project should be discussed first in an 48 | [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. 49 | * **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] 50 | as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr). 51 | 52 | ## Issue Submission Guidelines 53 | 54 | Before you submit your issue search the archive, maybe your question was already answered. 55 | 56 | If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize 57 | the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. 58 | 59 | The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to 60 | make it easier to understand and categorize the issue. 61 | 62 | ## Pull Request Submission Guidelines 63 | 64 | By submitting a pull request for a code or doc contribution, you need to have the right 65 | to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla] 66 | for details. 67 | 68 | Before you submit your pull request consider the following guidelines: 69 | 70 | * Search [GitHub][github-pr] for an open or closed Pull Request 71 | that relates to your submission. You don't want to duplicate effort. 72 | * Make your changes in a new git branch: 73 | 74 | ```shell 75 | git checkout -b my-fix-branch master 76 | ``` 77 | * Test your changes and, if relevant, expand the automated test suite. 78 | * Create your patch commit, including appropriate test cases. 79 | * If the changes affect public APIs, change or add relevant documentation. 80 | * Commit your changes using a descriptive commit message. 81 | 82 | ```shell 83 | git commit -a 84 | ``` 85 | Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. 86 | 87 | * Push your branch to GitHub: 88 | 89 | ```shell 90 | git push origin my-fix-branch 91 | ``` 92 | 93 | * In GitHub, send a pull request to `master` branch. This will trigger the continuous integration and run the test. 94 | We will also notify you if you have not yet signed the [contribution agreement][cla]. 95 | 96 | * If you find that the continunous integration has failed, look into the logs to find out 97 | if your changes caused test failures, the commit message was malformed etc. If you find that the 98 | tests failed or times out for unrelated reasons, you can ping a team member so that the build can be 99 | restarted. 100 | 101 | * If we suggest changes, then: 102 | 103 | * Make the required updates. 104 | * Test your changes and test cases. 105 | * Commit your changes to your branch (e.g. `my-fix-branch`). 106 | * Push the changes to your GitHub repository (this will update your Pull Request). 107 | 108 | You can also amend the initial commits and force push them to the branch. 109 | 110 | ```shell 111 | git rebase master -i 112 | git push origin my-fix-branch -f 113 | ``` 114 | 115 | This is generally easier to follow, but separate commits are useful if the Pull Request contains 116 | iterations that might be interesting to see side-by-side. 117 | 118 | That's it! Thank you for your contribution! 119 | 120 | ## Signing the Contributor License Agreement (CLA) 121 | 122 | Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done 123 | so before. It's a quick process, we promise, and you will be able to do it all online 124 | 125 | You can read [ProcessOne Contribution License Agreement][cla] in PDF. 126 | 127 | This is part of the legal framework of the open-source ecosystem that adds some red tape, 128 | but protects both the contributor and the company / foundation behind the project. It also 129 | gives us the option to relicense the code with a more permissive license in the future. 130 | 131 | 132 | [coc]: https://github.com/processone/docker-ejabberd/blob/master/CODE_OF_CONDUCT.md 133 | [stackoverflow]: https://stackoverflow.com/ 134 | [github]: https://github.com/processone/docker-ejabberd 135 | [github-issues]: https://github.com/processone/docker-ejabberd/issues 136 | [github-new-issue]: https://github.com/processone/docker-ejabberd/issues/new 137 | [github-pr]: https://github.com/processone/docker-ejabberd/pulls 138 | [cla]: https://www.process-one.net/resources/ejabberd-cla.pdf 139 | [license]: https://github.com/processone/docker-ejabberd/blob/master/LICENSE.txt 140 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | paths: 6 | - '.github/workflows/tests.yml' 7 | - 'ecs/**' 8 | - 'mix/**' 9 | pull_request: 10 | paths: 11 | - '.github/workflows/tests.yml' 12 | - 'ecs/**' 13 | - 'mix/**' 14 | 15 | env: 16 | REGISTRY_GHCR: ghcr.io 17 | REGISTRY_LOCAL: localhost:5000 18 | REGISTRY_DOCKER: docker.io 19 | MIX_IMAGENAME_GHCR: ${{ github.repository_owner }}/mix 20 | ECS_IMAGENAME_GHCR: ${{ github.repository_owner }}/ecs 21 | MIX_IMAGENAME_DOCKERHUB: ejabberd/mix 22 | ECS_IMAGENAME_DOCKERHUB: ejabberd/ecs 23 | 24 | jobs: 25 | 26 | build: 27 | name: Build 28 | runs-on: ubuntu-24.04 29 | strategy: 30 | fail-fast: true 31 | max-parallel: 1 32 | permissions: 33 | packages: write 34 | services: 35 | registry: 36 | image: registry:2 37 | ports: 38 | - 5000:5000 39 | 40 | steps: 41 | 42 | - name: Checkout code 43 | uses: actions/checkout@v4 44 | with: 45 | fetch-depth: 0 46 | 47 | - name: Login to GHCR 48 | uses: docker/login-action@v3 49 | with: 50 | registry: ${{ env.REGISTRY_GHCR }} 51 | username: ${{ github.actor }} 52 | password: ${{ secrets.GITHUB_TOKEN }} 53 | 54 | - name: Login to Docker Hub 55 | uses: docker/login-action@v3 56 | with: 57 | username: ${{ secrets.DOCKERHUB_USERNAME }} 58 | password: ${{ secrets.DOCKERHUB_TOKEN }} 59 | 60 | - name: Get git describe 61 | id: gitdescribe 62 | run: echo "ver=$(git describe --tags --exact-match 2>/dev/null || echo latest)" >> $GITHUB_OUTPUT 63 | 64 | - name: Extract mix metadata (tags, labels) 65 | id: mixmeta 66 | if: github.ref_type == 'tag' 67 | uses: docker/metadata-action@v5 68 | with: 69 | images: | 70 | ${{ env.REGISTRY_GHCR }}/${{ env.MIX_IMAGENAME_GHCR }} 71 | ${{ env.REGISTRY_DOCKER }}/${{ env.MIX_IMAGENAME_DOCKERHUB }} 72 | labels: | 73 | org.opencontainers.image.revision=${{ steps.gitdescribe.outputs.ver }} 74 | org.opencontainers.image.licenses=GPL-2.0 75 | org.opencontainers.image.vendor=ProcessOne 76 | 77 | - name: Extract ecs metadata (tags, labels) 78 | id: ecsmeta 79 | if: github.ref_type == 'tag' 80 | uses: docker/metadata-action@v5 81 | with: 82 | images: | 83 | ${{ env.REGISTRY_GHCR }}/${{ env.ECS_IMAGENAME_GHCR }} 84 | ${{ env.REGISTRY_DOCKER }}/${{ env.ECS_IMAGENAME_DOCKERHUB }} 85 | labels: | 86 | org.opencontainers.image.revision=${{ steps.gitdescribe.outputs.ver }} 87 | org.opencontainers.image.licenses=GPL-2.0 88 | org.opencontainers.image.vendor=ProcessOne 89 | 90 | - name: Prepare local tags 91 | id: localreg 92 | run: | 93 | tag="$(echo ${{ github.ref_name }} | sed -e 's|[/]\+|-|g')" 94 | echo "mixlocaltag=${{ env.REGISTRY_LOCAL }}/${{ env.MIX_IMAGENAME_GHCR }}:$tag" >> $GITHUB_OUTPUT 95 | echo "ecslocaltag=${{ env.REGISTRY_LOCAL }}/${{ env.ECS_IMAGENAME_GHCR }}:$tag" >> $GITHUB_OUTPUT 96 | 97 | - name: Set up Docker Buildx 98 | uses: docker/setup-buildx-action@v3 99 | with: 100 | driver-opts: network=host 101 | 102 | - name: Build and push local mix image 103 | uses: docker/build-push-action@v5 104 | with: 105 | build-args: | 106 | VERSION=${{ steps.gitdescribe.outputs.ver }} 107 | cache-from: type=gha 108 | cache-to: type=gha,mode=max 109 | context: mix 110 | labels: ${{ steps.mixmeta.outputs.labels }} 111 | platforms: linux/amd64 112 | push: true 113 | tags: | 114 | ${{ steps.localreg.outputs.mixlocaltag }} 115 | 116 | - name: Prepare ecs Dockerfile 117 | run: sed -i 's|docker.io/ejabberd/mix|${{ steps.localreg.outputs.mixlocaltag }}|g' ecs/Dockerfile 118 | 119 | - name: Build and push local ecs image 120 | uses: docker/build-push-action@v5 121 | with: 122 | build-args: | 123 | VERSION=${{ steps.gitdescribe.outputs.ver }} 124 | cache-from: type=gha 125 | cache-to: type=gha,mode=max 126 | context: ecs 127 | labels: ${{ steps.ecsmeta.outputs.labels }} 128 | platforms: linux/amd64 129 | push: true 130 | tags: | 131 | ${{ steps.localreg.outputs.ecslocaltag }} 132 | 133 | - name: Run ecs image 134 | run: | 135 | docker images 136 | docker run --name ejabberd -d -p 5222:5222 ${{ steps.localreg.outputs.ecslocaltag }} 137 | 138 | - name: Wait ejabberd started 139 | run: | 140 | docker exec ejabberd bin/ejabberdctl started 141 | 142 | - name: Check ecs results 143 | if: always() 144 | run: | 145 | docker ps -s 146 | docker logs ejabberd 147 | docker logs ejabberd | grep -q "Start accepting TCP connections" || exit 1 148 | docker logs ejabberd | grep -q "error" && exit 1 || exit 0 149 | docker logs ejabberd | grep -q "Error" && exit 1 || exit 0 150 | 151 | - name: Save image 152 | run: | 153 | docker image save ${{ steps.localreg.outputs.ecslocaltag }} --output ejabberd-latest.tar 154 | 155 | - name: Upload image 156 | uses: actions/upload-artifact@v4 157 | with: 158 | name: ejabberd-image 159 | path: ejabberd-latest.tar 160 | 161 | - run: | 162 | echo "::notice::To get this image, download ejabberd-image.zip, "\ 163 | "uncompress it and run: " \ 164 | "docker image load -i ejabberd-latest.tar" 165 | 166 | - name: Build and push mix image 167 | uses: docker/build-push-action@v5 168 | if: github.ref_type == 'tag' 169 | with: 170 | build-args: | 171 | VERSION=${{ steps.gitdescribe.outputs.ver }} 172 | cache-from: type=gha 173 | cache-to: type=gha,mode=max 174 | context: mix 175 | labels: ${{ steps.mixmeta.outputs.labels }} 176 | platforms: linux/amd64 177 | push: true 178 | tags: | 179 | ${{ steps.mixmeta.outputs.tags }} 180 | 181 | - name: Build and push ecs image 182 | uses: docker/build-push-action@v5 183 | if: github.ref_type == 'tag' 184 | with: 185 | build-args: | 186 | VERSION=${{ steps.gitdescribe.outputs.ver }} 187 | cache-from: type=gha 188 | cache-to: type=gha,mode=max 189 | context: ecs 190 | labels: ${{ steps.ecsmeta.outputs.labels }} 191 | platforms: linux/amd64 192 | push: true 193 | tags: | 194 | ${{ steps.ecsmeta.outputs.tags }} 195 | -------------------------------------------------------------------------------- /ecs/conf/ejabberdctl.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # In this file you can configure options that are passed by ejabberdctl 3 | # to the erlang runtime system when starting ejabberd 4 | # 5 | 6 | #' POLL: Kernel polling ([true|false]) 7 | # 8 | # The kernel polling option requires support in the kernel. 9 | # Additionally, you need to enable this feature while compiling Erlang. 10 | # 11 | # Default: true 12 | # 13 | #POLL=true 14 | 15 | #. 16 | #' ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports 17 | # 18 | # ejabberd consumes two or three ports for every connection, either 19 | # from a client or from another XMPP server. So take this into 20 | # account when setting this limit. 21 | # 22 | # Default: 65536 (or 8196 on Windows) 23 | # Maximum: 268435456 24 | # 25 | #ERL_MAX_PORTS=65536 26 | 27 | #. 28 | #' FIREWALL_WINDOW: Range of allowed ports to pass through a firewall 29 | # 30 | # If ejabberd is configured to run in cluster, and a firewall is blocking ports, 31 | # it's possible to make Erlang use a defined range of port (instead of dynamic 32 | # ports) for node communication. 33 | # 34 | # Default: not defined 35 | # Example: 4200-4210 36 | # 37 | #FIREWALL_WINDOW= 38 | 39 | #. 40 | #' INET_DIST_INTERFACE: IP address where this Erlang node listens other nodes 41 | # 42 | # This communication is used by ejabberdctl command line tool, 43 | # and in a cluster of several ejabberd nodes. 44 | # 45 | # Default: 0.0.0.0 46 | # 47 | #INET_DIST_INTERFACE=127.0.0.1 48 | 49 | #. 50 | #' ERL_DIST_PORT: Port number for Erlang distribution 51 | # 52 | # For Erlang distribution, clustering and ejabberdctl usage, the 53 | # Erlang VM listens in a random TCP port number, and the Erlang Port 54 | # Mapper Daemon (EPMD) is spawned and used to determine this port 55 | # number. 56 | # 57 | # ERL_DIST_PORT can define this port number. In that case, EPMD is 58 | # not spawned during ejabberd startup, and ERL_EPMD_ADDRESS is 59 | # ignored. ERL_DIST_PORT must be set to the same port number during 60 | # ejabberd startup and when calling ejabberdctl. This feature 61 | # requires at least Erlang/OTP 23.1. 62 | # 63 | # Default: not defined 64 | # 65 | #ERL_DIST_PORT=5210 66 | 67 | #. 68 | #' ERL_EPMD_ADDRESS: IP addresses where EPMD listens for connections 69 | # 70 | # This environment variable may be set to a comma-separated 71 | # list of IP addresses, in which case the EPMD daemon 72 | # will listen only on the specified address(es) and on the 73 | # loopback address (which is implicitly added to the list if it 74 | # has not been specified). The default behaviour is to listen on 75 | # all available IP addresses. 76 | # 77 | # Default: 0.0.0.0 78 | # 79 | #ERL_EPMD_ADDRESS=127.0.0.1 80 | 81 | #. 82 | #' ERL_PROCESSES: Maximum number of Erlang processes 83 | # 84 | # Erlang consumes a lot of lightweight processes. If there is a lot of activity 85 | # on ejabberd so that the maximum number of processes is reached, people will 86 | # experience greater latency times. As these processes are implemented in 87 | # Erlang, and therefore not related to the operating system processes, you do 88 | # not have to worry about allowing a huge number of them. 89 | # 90 | # Default: 262144 91 | # Maximum: 268435456 92 | # 93 | #ERL_PROCESSES=262144 94 | 95 | #. 96 | #' ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables 97 | # 98 | # The number of concurrent ETS and Mnesia tables is limited. When the limit is 99 | # reached, errors will appear in the logs: 100 | # ** Too many db tables ** 101 | # You can safely increase this limit when starting ejabberd. It impacts memory 102 | # consumption but the difference will be quite small. 103 | # 104 | # Default: 2053 105 | # 106 | #ERL_MAX_ETS_TABLES=2053 107 | 108 | #. 109 | #' ERL_OPTIONS: Additional Erlang options 110 | # 111 | # The next variable allows to specify additional options passed to 112 | # all commands using erlang interpreter. This applies to starting 113 | # ejabberd server itself but also auxiliary commands like for example 114 | # starting debug shell. See erl(1) for list of commands that can be 115 | # used here. 116 | # 117 | # It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you 118 | # want to add local modules in this path. 119 | # 120 | # Default: "" 121 | # 122 | #ERL_OPTIONS="" 123 | 124 | #. 125 | #' EJABBERD_OPTS: Additional Erlang options to start ejabberd 126 | # 127 | # The next variable allows to specify additional options passed to erlang while 128 | # starting ejabberd. Some useful options are -noshell, -detached, -heart. When 129 | # ejabberd is started from an init.d script options -noshell and -detached are 130 | # added implicitly. See erl(1) for more info. 131 | # 132 | # For example you can use value "-heart -env HEART_BEAT_TIMEOUT 120 -env ERL_CRASH_DUMP_SECONDS 60" 133 | # 134 | # Default: "" 135 | # 136 | #EJABBERD_OPTS="" 137 | 138 | #. 139 | #' ERLANG_NODE: Erlang node name 140 | # 141 | # The next variable allows to explicitly specify erlang node for ejabberd 142 | # It can be given in different formats: 143 | # ERLANG_NODE=ejabberd 144 | # Lets erlang add hostname to the node (ejabberd uses short name in this case) 145 | # ERLANG_NODE=ejabberd@hostname 146 | # Erlang uses node name as is (so make sure that hostname is a real 147 | # machine hostname or you'll not be able to control ejabberd) 148 | # ERLANG_NODE=ejabberd@hostname.domainname 149 | # The same as previous, but erlang will use long hostname 150 | # (see erl (1) manual for details) 151 | # 152 | # Default: ejabberd@localhost 153 | # 154 | #ERLANG_NODE=ejabberd@$(hostname -s) 155 | 156 | #. 157 | #' ERLANG_COOKIE: Erlang cookie for inter-node communication 158 | # 159 | # When using ejabberd in cluster, you need all nodes to share 160 | # the same erlang cookie. If not defined, a random value is used 161 | # at node first start, which prevent clustering to work. Setting 162 | # a defined cookie for all your nodes is needed to setup cluster. 163 | # 164 | # Default: "" 165 | # 166 | #ERLANG_COOKIE="" 167 | 168 | #. 169 | #. 170 | #' EJABBERD_PID_PATH: ejabberd PID file 171 | # 172 | # Indicate the full path to the ejabberd Process identifier (PID) file. 173 | # If this variable is defined, ejabberd writes the PID file when starts, 174 | # and deletes it when stops. 175 | # Remember to create the directory and grant write permission to ejabberd. 176 | # 177 | # Default: don't write PID file 178 | # 179 | #EJABBERD_PID_PATH=/var/run/ejabberd/ejabberd.pid 180 | 181 | #. 182 | #' EJABBERD_CONFIG_PATH: ejabberd configuration file 183 | # 184 | # Specify the full path to the ejabberd configuration file. If the file name has 185 | # yml or yaml extension, it is parsed as a YAML file; otherwise, Erlang syntax is 186 | # expected. 187 | # 188 | # Default: $ETC_DIR/ejabberd.yml 189 | # 190 | #EJABBERD_CONFIG_PATH=/etc/ejabberd/ejabberd.yml 191 | 192 | #. 193 | #' CONTRIB_MODULES_PATH: contributed ejabberd modules path 194 | # 195 | # Specify the full path to the contributed ejabberd modules. If the path is not 196 | # defined, ejabberd will use ~/.ejabberd-modules in home of user running ejabberd. 197 | # 198 | # Default: $HOME/.ejabberd-modules 199 | # 200 | #CONTRIB_MODULES_PATH=/opt/ejabberd-modules 201 | 202 | #. 203 | #' CONTRIB_MODULES_CONF_DIR: configuration directory for contributed modules 204 | # 205 | # Specify the full path to the configuration directory for contributed ejabberd 206 | # modules. In order to configure a module named mod_foo, a mod_foo.yml file can 207 | # be created in this directory. This file will then be used instead of the 208 | # default configuration file provided with the module. 209 | # 210 | # Default: $CONTRIB_MODULES_PATH/conf 211 | # 212 | #CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules 213 | 214 | #. 215 | #' 216 | # vim: foldmarker=#',#. foldmethod=marker: 217 | -------------------------------------------------------------------------------- /ecs/conf/ejabberd.yml: -------------------------------------------------------------------------------- 1 | ### 2 | ### ejabberd configuration file 3 | ### 4 | ### The parameters used in this configuration file are explained at 5 | ### 6 | ### https://docs.ejabberd.im/admin/configuration 7 | ### 8 | ### The configuration file is written in YAML. 9 | ### ******************************************************* 10 | ### ******* !!! WARNING !!! ******* 11 | ### ******* YAML IS INDENTATION SENSITIVE ******* 12 | ### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY ******* 13 | ### ******************************************************* 14 | ### Refer to http://en.wikipedia.org/wiki/YAML for the brief description. 15 | ### 16 | 17 | define_macro: 18 | HOST: localhost 19 | ADMIN: "admin@localhost" 20 | PORT_C2S: 5222 21 | PORT_C2S_TLS: 5223 22 | PORT_S2S: 5269 23 | PORT_HTTP_TLS: 5443 24 | PORT_HTTP: 5280 25 | PORT_BROWSER: 1880 26 | PORT_STUN: 5478 27 | PORT_TURN_MIN: 50000 28 | PORT_TURN_MAX: 50099 29 | PORT_MQTT: 1883 30 | PORT_PROXY65: 7777 31 | 32 | hosts: 33 | - HOST 34 | 35 | loglevel: info 36 | 37 | certfiles: 38 | - /home/ejabberd/conf/server.pem 39 | 40 | ca_file: "/home/ejabberd/conf/cacert.pem" 41 | 42 | ## When using let's encrypt to generate certificates 43 | ##certfiles: 44 | ## - /etc/letsencrypt/live/localhost/fullchain.pem 45 | ## - /etc/letsencrypt/live/localhost/privkey.pem 46 | ## 47 | ##ca_file: "/etc/letsencrypt/live/localhost/fullchain.pem" 48 | 49 | listen: 50 | - 51 | port: PORT_C2S 52 | ip: "::" 53 | module: ejabberd_c2s 54 | max_stanza_size: 262144 55 | shaper: c2s_shaper 56 | access: c2s 57 | starttls_required: true 58 | - 59 | port: PORT_C2S_TLS 60 | ip: "::" 61 | module: ejabberd_c2s 62 | max_stanza_size: 262144 63 | shaper: c2s_shaper 64 | access: c2s 65 | tls: true 66 | - 67 | port: PORT_S2S 68 | ip: "::" 69 | module: ejabberd_s2s_in 70 | max_stanza_size: 524288 71 | shaper: s2s_shaper 72 | - 73 | port: PORT_HTTP_TLS 74 | ip: "::" 75 | module: ejabberd_http 76 | tls: true 77 | request_handlers: 78 | /admin: ejabberd_web_admin 79 | /api: mod_http_api 80 | /bosh: mod_bosh 81 | /captcha: ejabberd_captcha 82 | /upload: mod_http_upload 83 | /ws: ejabberd_http_ws 84 | /oauth: ejabberd_oauth 85 | - 86 | port: PORT_HTTP 87 | ip: "::" 88 | module: ejabberd_http 89 | request_handlers: 90 | /admin: ejabberd_web_admin 91 | - 92 | port: PORT_BROWSER 93 | ip: "::" 94 | module: ejabberd_http 95 | request_handlers: 96 | /: ejabberd_web_admin 97 | - 98 | port: PORT_STUN 99 | ip: "::" 100 | transport: udp 101 | module: ejabberd_stun 102 | use_turn: true 103 | turn_min_port: PORT_TURN_MIN 104 | turn_max_port: PORT_TURN_MAX 105 | ## The server's public IPv4 address: 106 | # turn_ipv4_address: "203.0.113.3" 107 | ## The server's public IPv6 address: 108 | # turn_ipv6_address: "2001:db8::3" 109 | - 110 | port: PORT_MQTT 111 | ip: "::" 112 | module: mod_mqtt 113 | backlog: 1000 114 | ## 115 | ## https://docs.ejabberd.im/admin/configuration/#stun-and-turn 116 | ## ejabberd_stun: Handles STUN Binding requests 117 | ## 118 | ##- 119 | ## port: 3478 120 | ## ip: "0.0.0.0" 121 | ## transport: udp 122 | ## module: ejabberd_stun 123 | ## use_turn: true 124 | ## turn_ip: "{{ IP }}" 125 | ## auth_type: user 126 | ## auth_realm: "example.com" 127 | ##- 128 | ## port: 3478 129 | ## ip: "0.0.0.0" 130 | ## module: ejabberd_stun 131 | ## use_turn: true 132 | ## turn_ip: "{{ IP }}" 133 | ## auth_type: user 134 | ## auth_realm: "example.com" 135 | ##- 136 | ## port: 5349 137 | ## ip: "0.0.0.0" 138 | ## module: ejabberd_stun 139 | ## certfile: "/home/ejabberd/conf/server.pem" 140 | ## tls: true 141 | ## use_turn: true 142 | ## turn_ip: "{{ IP }}" 143 | ## auth_type: user 144 | ## auth_realm: "example.com" 145 | ## 146 | ## https://docs.ejabberd.im/admin/configuration/#sip 147 | ## To handle SIP (VOIP) requests: 148 | ## 149 | ##- 150 | ## port: 5060 151 | ## ip: "0.0.0.0" 152 | ## transport: udp 153 | ## module: ejabberd_sip 154 | ##- 155 | ## port: 5060 156 | ## ip: "0.0.0.0" 157 | ## module: ejabberd_sip 158 | ##- 159 | ## port: 5061 160 | ## ip: "0.0.0.0" 161 | ## module: ejabberd_sip 162 | ## tls: true 163 | 164 | s2s_use_starttls: optional 165 | 166 | acl: 167 | local: 168 | user_regexp: "" 169 | loopback: 170 | ip: 171 | - 127.0.0.0/8 172 | - ::1/128 173 | admin: 174 | user: 175 | - ADMIN 176 | 177 | access_rules: 178 | local: 179 | allow: local 180 | c2s: 181 | deny: blocked 182 | allow: all 183 | announce: 184 | allow: admin 185 | configure: 186 | allow: admin 187 | muc_create: 188 | allow: local 189 | pubsub_createnode: 190 | allow: local 191 | trusted_network: 192 | allow: loopback 193 | 194 | api_permissions: 195 | "console commands": 196 | from: ejabberd_ctl 197 | who: all 198 | what: "*" 199 | "webadmin commands": 200 | from: ejabberd_web_admin 201 | who: admin 202 | what: "*" 203 | "adhoc commands": 204 | from: mod_adhoc_api 205 | who: admin 206 | what: "*" 207 | "http access": 208 | from: mod_http_api 209 | who: 210 | access: 211 | allow: 212 | - acl: loopback 213 | - acl: admin 214 | oauth: 215 | scope: "ejabberd:admin" 216 | access: 217 | allow: 218 | - acl: loopback 219 | - acl: admin 220 | what: 221 | - "*" 222 | - "!stop" 223 | - "!start" 224 | "public commands": 225 | who: 226 | ip: 127.0.0.1/8 227 | what: 228 | - status 229 | - connected_users_number 230 | 231 | shaper: 232 | normal: 233 | rate: 3000 234 | burst_size: 20000 235 | fast: 100000 236 | 237 | shaper_rules: 238 | max_user_sessions: 10 239 | max_user_offline_messages: 240 | 5000: admin 241 | 100: all 242 | c2s_shaper: 243 | none: admin 244 | normal: all 245 | s2s_shaper: fast 246 | 247 | acme: 248 | contact: "mailto:example-admin@example.com" 249 | ca_url: "https://acme-staging-v02.api.letsencrypt.org/directory" 250 | 251 | modules: 252 | mod_adhoc: {} 253 | mod_adhoc_api: {} 254 | mod_admin_extra: {} 255 | mod_announce: 256 | access: announce 257 | mod_avatar: {} 258 | mod_blocking: {} 259 | mod_bosh: {} 260 | mod_caps: {} 261 | mod_carboncopy: {} 262 | mod_client_state: {} 263 | mod_configure: {} 264 | mod_disco: {} 265 | mod_fail2ban: {} 266 | mod_http_api: {} 267 | mod_http_upload: 268 | put_url: https://@HOST_URL_ENCODE@:5443/upload 269 | custom_headers: 270 | "Access-Control-Allow-Origin": "https://@HOST@" 271 | "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS" 272 | "Access-Control-Allow-Headers": "Content-Type" 273 | mod_last: {} 274 | mod_mam: 275 | ## Mnesia is limited to 2GB, better to use an SQL backend 276 | ## For small servers SQLite is a good fit and is very easy 277 | ## to configure. Uncomment this when you have SQL configured: 278 | ## db_type: sql 279 | assume_mam_usage: true 280 | default: always 281 | mod_mqtt: {} 282 | mod_muc: 283 | access: 284 | - allow 285 | access_admin: 286 | - allow: admin 287 | access_create: muc_create 288 | access_persistent: muc_create 289 | access_mam: 290 | - allow 291 | default_room_options: 292 | mam: true 293 | mod_muc_admin: {} 294 | mod_muc_occupantid: {} 295 | mod_offline: 296 | access_max_user_messages: max_user_offline_messages 297 | mod_ping: {} 298 | mod_privacy: {} 299 | mod_private: {} 300 | mod_proxy65: 301 | access: local 302 | max_connections: 5 303 | port: PORT_PROXY65 304 | mod_pubsub: 305 | access_createnode: pubsub_createnode 306 | plugins: 307 | - flat 308 | - pep 309 | force_node_config: 310 | ## Avoid buggy clients to make their bookmarks public 311 | storage:bookmarks: 312 | access_model: whitelist 313 | mod_push: {} 314 | mod_push_keepalive: {} 315 | mod_register: 316 | ## Only accept registration requests from the "trusted" 317 | ## network (see access_rules section above). 318 | ## Think twice before enabling registration from any 319 | ## address. See the Jabber SPAM Manifesto for details: 320 | ## https://github.com/ge0rg/jabber-spam-fighting-manifesto 321 | ip_access: trusted_network 322 | mod_roster: 323 | versioning: true 324 | mod_sip: {} 325 | mod_s2s_bidi: {} 326 | mod_s2s_dialback: {} 327 | mod_shared_roster: {} 328 | mod_stream_mgmt: 329 | resend_on_timeout: if_offline 330 | mod_stun_disco: {} 331 | mod_vcard: {} 332 | mod_vcard_xupdate: {} 333 | mod_version: 334 | show_os: false 335 | 336 | ### Local Variables: 337 | ### mode: yaml 338 | ### End: 339 | ### vim: set filetype=yaml tabstop=8 340 | -------------------------------------------------------------------------------- /ecs/ejabberd-docker-install.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: 4 | :: ejabberd Docker installer for Windows 5 | :: ------------------------------------- 6 | :: v0.3 7 | :: 8 | :: This batch script downloads an ejabberd docker image 9 | :: and setups a docker container to run ejabberd. 10 | 11 | :: 12 | :: 1. Download and install Docker: 13 | :: 14 | :: If you use Windows 10, download Docker Desktop from: 15 | :: https://www.docker.com/ 16 | :: 17 | :: If you use Windows 7 or 8, download Docker Toolbox from: 18 | :: https://github.com/docker/toolbox/releases 19 | :: After installation, run Docker Quickstart Installer 20 | :: 21 | 22 | :: 23 | :: 2. Edit those options: 24 | 25 | :: Directory where your ejabberd deployment files will be installed 26 | :: (configuration, database, logs, ...) 27 | :: 28 | :: In Windows 10 you can configure the path: 29 | 30 | set INSTALL_DIR_WINDOWS10=C:\ejabberd 31 | 32 | :: In older Windows, not configurable, it will be installed in: 33 | :: C:\Users\%USERNAME%\ejabberd 34 | 35 | :: Please enter the desired ejabberd domain name. 36 | :: The domain is the visible attribute that is added to the username 37 | :: to form the Jabber Identifier (for example: user@example.net). 38 | :: This computer must be known on the network with this address name. 39 | :: You can later add more in conf/ejabberd.yml 40 | 41 | set HOST=localhost 42 | 43 | :: Please enter the administrator username for the current 44 | :: ejabberd installation. A Jabber account with this username 45 | :: will be created and granted administrative privileges. 46 | :: Don't use blankspaces in the username. 47 | 48 | set USER=admin 49 | 50 | :: Please provide a password for that new administrator account 51 | 52 | set PASSWORD= 53 | 54 | :: By default this downloads 'latest' ejabberd version, 55 | :: but you can set a specific version, for example 21.01 56 | 57 | set VERSION=latest 58 | 59 | :: This tells docker what ports ejabberd will use. 60 | :: You can later configure them in conf/ejabberd.yml 61 | 62 | set PORTS=5180 5222 5269 5443 63 | 64 | :: 65 | :: 3. Now save this script and run it. 66 | :: 67 | 68 | :: 69 | :: 4. When installation is completed: 70 | :: 71 | :: If using Windows 10, open Docker Desktop and you can: 72 | :: 73 | :: - (>) START the ejabberd container 74 | :: - Enter WebAdmin: click the ([->]) OPEN IN BROWSER button 75 | :: - To try ejabberdctl, click the (>_) CLI button, then: bin/ejabberdctl 76 | :: - ([]) STOP the ejabberd container 77 | :: 78 | :: If using an old Windows, open Kitematic and you can: 79 | :: 80 | :: - START the ejabberd container 81 | :: - Open your configuration, logs, ... in Settings > Volumes 82 | :: - Enter WebAdmin in Settings > Hostname/Ports > click on the 5180 port 83 | :: - Try ejabberdctl in EXEC, then: bin/ejabberdctl 84 | :: - STOP the ejabberd container 85 | :: 86 | :: You can delete the container and create it again running this script, 87 | :: the configuration and database are maintained. 88 | :: 89 | 90 | ::=============================================================== 91 | :: Check Windows version 92 | :: 93 | ::=============================================================== 94 | 95 | set INSTALL_DIR_DOCKER=c/Users/%USERNAME%/ejabberd 96 | 97 | for /f "tokens=4-5 delims=. " %%i in ('ver') do set WVERSION=%%i.%%j 98 | if "%wversion%" == "10.0" ( 99 | echo === Preparing paths to install in Windows 10... 100 | set INSTALL_DIR=%INSTALL_DIR_WINDOWS10% 101 | set VC=-v %INSTALL_DIR_WINDOWS10%\conf:/home/ejabberd/conf 102 | set VD=-v %INSTALL_DIR_WINDOWS10%\database:/home/ejabberd/database 103 | set VL=-v %INSTALL_DIR_WINDOWS10%\logs:/home/ejabberd/logs 104 | set VM=-v %INSTALL_DIR_WINDOWS10%\ejabberd-modules:/home/ejabberd/.ejabberd-modules 105 | set DOCKERDOWNLOAD="First download and install Docker Desktop from https://www.docker.com/" 106 | ) else ( 107 | echo === Preparing paths to install in Windows older than 10... 108 | set INSTALL_DIR=C:\Users\%USERNAME%\ejabberd 109 | set VC=-v "/%INSTALL_DIR_DOCKER%/conf:/home/ejabberd/conf" 110 | set VD=-v "/%INSTALL_DIR_DOCKER%/database:/home/ejabberd/database" 111 | set VL=-v "/%INSTALL_DIR_DOCKER%/logs:/home/ejabberd/logs" 112 | set VM=-v "/%INSTALL_DIR_DOCKER%/ejabberd-modules:/home/ejabberd/.ejabberd-modules" 113 | set DOCKERDOWNLOAD="First download and install Docker Toolbox from https://github.com/docker/toolbox/releases" 114 | ) 115 | set VOLUMES=%VC% %VD% %VL% %VM% 116 | 117 | ::=============================================================== 118 | :: Check docker is installed 119 | :: 120 | ::=============================================================== 121 | 122 | docker version >NUL 123 | if %ERRORLEVEL% NEQ 0 ( 124 | echo. 125 | echo === ERROR: It seems docker is not installed!!! 126 | echo. 127 | echo %DOCKERDOWNLOAD% 128 | echo === Then try to run this script again. 129 | echo. 130 | pause 131 | exit 1 132 | ) 133 | 134 | ::=============================================================== 135 | :: Check install options are correctly set 136 | :: 137 | ::=============================================================== 138 | 139 | if [%PASSWORD%]==[] ( 140 | echo. 141 | echo === ERROR: PASSWORD not set!!! 142 | echo. 143 | echo === Please edit this script and set the PASSWORD. 144 | echo === Then try to run this script again. 145 | echo. 146 | pause 147 | exit 1 148 | ) 149 | 150 | ::=============================================================== 151 | :: Download Docker image 152 | :: 153 | ::=============================================================== 154 | 155 | set IMAGE=ejabberd/ecs:%VERSION% 156 | 157 | echo. 158 | echo === Checking if the '%IMAGE%' docker image was already downloaded... 159 | docker image history %IMAGE% >NUL 160 | if %ERRORLEVEL% NEQ 0 ( 161 | echo === The '%IMAGE%' docker image was not downloaded yet. 162 | echo. 163 | echo === Downloading the '%IMAGE%' docker image, please wait... 164 | docker pull %IMAGE% 165 | ) else ( 166 | echo === The '%IMAGE%' docker image was already downloaded. 167 | ) 168 | 169 | ::=============================================================== 170 | :: Create preliminary container 171 | :: 172 | ::=============================================================== 173 | 174 | echo. 175 | echo === Checking if the 'ejabberd' docker container already exists... 176 | docker container logs ejabberd 177 | if %ERRORLEVEL% EQU 0 ( 178 | echo. 179 | echo === The 'ejabberd' docker container already exists. 180 | echo === Nothing to do, so installation finishes now. 181 | echo === You can go to Docker Desktop and start the 'ejabberd' container. 182 | echo. 183 | pause 184 | exit 1 185 | ) else ( 186 | echo === The 'ejabberd' docker container doesn't yet exist, 187 | echo === so let's continue the installation process. 188 | ) 189 | 190 | echo. 191 | if exist %INSTALL_DIR% ( 192 | echo === The INSTALL_DIR %INSTALL_DIR% already exists. 193 | echo === No need to create the preliminary 'ejabberd-pre' image. 194 | ) else ( 195 | echo === The INSTALL_DIR %INSTALL_DIR% doesn't exist. 196 | echo === Let's create the preliminary 'ejabberd-pre' image. 197 | CALL :create-ejabberd-pre 198 | ) 199 | 200 | ::=============================================================== 201 | :: Create final container 202 | :: 203 | ::=============================================================== 204 | 205 | echo. 206 | echo === Creating the final 'ejabberd' docker container using %IMAGE% image... 207 | 208 | setlocal EnableDelayedExpansion 209 | set PS= 210 | for %%a in (%PORTS%) do ( 211 | set PS=!PS! -p %%a:%%a 212 | ) 213 | 214 | docker create --name ejabberd --hostname localhost %PS% %VOLUMES% %IMAGE% 215 | 216 | echo. 217 | echo === Installation completed. 218 | echo. 219 | pause 220 | 221 | EXIT /B %ERRORLEVEL% 222 | 223 | ::=============================================================== 224 | :: Function to create preliminary container 225 | :: 226 | ::=============================================================== 227 | 228 | :create-ejabberd-pre 229 | 230 | echo. 231 | echo === Creating a preliminary 'ejabberd-pre' docker image using %IMAGE% image... 232 | docker create --name ejabberd-pre --hostname localhost %IMAGE% 233 | 234 | echo. 235 | echo === Now 'ejabberd-pre' will be started. 236 | docker container start ejabberd-pre 237 | 238 | echo. 239 | echo === Waiting ejabberd to be running... 240 | set /A timeout = 10 241 | set status=4 242 | goto :while 243 | 244 | :statusstart 245 | docker exec -it ejabberd-pre bin/ejabberdctl status 246 | goto :statusend 247 | 248 | :while 249 | if %status% GTR 0 ( 250 | echo. 251 | timeout /t 1 /nobreak >NUL 252 | set /A timeout = timeout - 1 253 | if %timeout% EQU 0 ( 254 | set status=-1 255 | ) else ( 256 | goto :statusstart 257 | :statusend 258 | set status=%ERRORLEVEL% 259 | ) 260 | goto :while 261 | ) 262 | 263 | echo. 264 | echo === Setting a few options... 265 | docker exec -it ejabberd-pre sed -i "s!- localhost!- %HOST%!g" conf/ejabberd.yml 266 | docker exec -it ejabberd-pre sed -i "s!admin@localhost!%USER%@%HOST%!g" conf/ejabberd.yml 267 | docker exec -it ejabberd-pre sed -i "s!5280!5180!g" conf/ejabberd.yml 268 | docker exec -it ejabberd-pre sed -i "s!\"/admin\"!/!g" conf/ejabberd.yml 269 | docker exec -it ejabberd-pre bin/ejabberdctl reload_config 270 | 271 | echo. 272 | echo === Registering the administrator account... 273 | docker exec -it ejabberd-pre bin/ejabberdctl register %USER% %HOST% %PASSWORD% 274 | docker exec -it ejabberd-pre bin/ejabberdctl stop 275 | 276 | echo. 277 | echo === Copying conf, database, logs... 278 | mkdir %INSTALL_DIR% 279 | mkdir %INSTALL_DIR%\conf 280 | mkdir %INSTALL_DIR%\database 281 | mkdir %INSTALL_DIR%\logs 282 | mkdir %INSTALL_DIR%\ejabberd-modules 283 | docker cp ejabberd-pre:/home/ejabberd/conf/ %INSTALL_DIR% 284 | docker cp ejabberd-pre:/home/ejabberd/database/ %INSTALL_DIR% 285 | docker cp ejabberd-pre:/home/ejabberd/logs/ %INSTALL_DIR% 286 | 287 | echo. 288 | echo === Deleting the preliminary 'ejabberd-pre' docker image... 289 | docker stop ejabberd-pre 290 | docker rm ejabberd-pre 291 | 292 | EXIT /B 0 293 | -------------------------------------------------------------------------------- /ecs/bin/ejabberdctl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # define default configuration 4 | POLL=true 5 | ERL_MAX_PORTS=32000 6 | ERL_PROCESSES=250000 7 | ERL_MAX_ETS_TABLES=1400 8 | FIREWALL_WINDOW="4370-4379" 9 | INET_DIST_INTERFACE="" 10 | ERLANG_NODE=ejabberd@localhost 11 | EJABBERD_BYPASS_WARNINGS=true 12 | 13 | # define default environment variables 14 | ROOT_DIR="/home/ejabberd" 15 | HOME_DIR="$ROOT_DIR" 16 | ERL="$ROOT_DIR"/bin/erl 17 | IEX="$ROOT_DIR"/bin/iex 18 | EPMD="$ROOT_DIR"/bin/epmd 19 | INSTALLUSER=ejabberd 20 | 21 | # check the proper system user is used 22 | case $(id -un) in 23 | "$INSTALLUSER") 24 | EXEC_CMD="as_current_user" 25 | [ -e "$HOME"/conf/ejabberd.yml ] && HOME_DIR="$HOME" 26 | ;; 27 | root) 28 | if [ -n "$INSTALLUSER" ] ; then 29 | EXEC_CMD="as_install_user" 30 | HOME=$(su - ejabberd -c pwd) 31 | [ -e "$HOME"/conf/ejabberd.yml ] && HOME_DIR="$HOME" 32 | else 33 | EXEC_CMD="as_current_user" 34 | echo "WARNING: It is not recommended to run ejabberd as root" >&2 35 | fi 36 | ;; 37 | *) 38 | if [ -n "$INSTALLUSER" ] ; then 39 | echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2 40 | exit 7 41 | else 42 | EXEC_CMD="as_current_user" 43 | fi 44 | ;; 45 | esac 46 | 47 | # parse command line parameters 48 | while [ $# -gt 0 ]; do 49 | case $1 in 50 | -n|--node) ERLANG_NODE_ARG=$2; shift 2;; 51 | -s|--spool) SPOOL_DIR=$2; shift 2;; 52 | -l|--logs) LOGS_DIR=$2; shift 2;; 53 | -f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;; 54 | -c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;; 55 | -d|--config-dir) ETC_DIR=$2; shift 2;; 56 | -t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;; 57 | *) break;; 58 | esac 59 | done 60 | 61 | # define ejabberd variables if not already defined from the command line 62 | : "${ETC_DIR:="$HOME_DIR/conf"}" 63 | : "${LOGS_DIR:="$HOME_DIR/logs"}" 64 | : "${EJABBERD_CONFIG_PATH:="$ETC_DIR/ejabberd.yml"}" 65 | : "${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR/ejabberdctl.cfg"}" 66 | # Allows passing extra Erlang command-line arguments in vm.args file 67 | : "${VMARGS:="$ETC_DIR/vm.args"}" 68 | [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" 69 | [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" 70 | [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" 71 | : "${SPOOL_DIR:="$HOME_DIR/database"}" 72 | : "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}" 73 | 74 | # backward support for old mnesia spool dir path 75 | : "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}" 76 | [ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD" 77 | 78 | [ -n "$ERLANG_COOKIE" ] && [ ! -f "$HOME"/.erlang.cookie ] && { 79 | echo "$ERLANG_COOKIE" > "$HOME"/.erlang.cookie 80 | chmod 400 "$HOME"/.erlang.cookie 81 | } 82 | 83 | # define erl parameters 84 | ERLANG_OPTS="-boot_var RELEASE_LIB ../lib +K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS" 85 | if [ -n "$FIREWALL_WINDOW" ] ; then 86 | ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}" 87 | fi 88 | if [ -n "$INET_DIST_INTERFACE" ] ; then 89 | INET_DIST_INTERFACE2=$("$ERL" -boot start_clean -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt) 90 | if [ -n "$INET_DIST_INTERFACE2" ] ; then 91 | ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2" 92 | fi 93 | fi 94 | [ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -erl_epmd_port $ERL_DIST_PORT -start_epmd false" 95 | ERL_LIBS="$ROOT_DIR/lib" 96 | # if vm.args file exists in config directory, pass it to Erlang VM 97 | [ -f "$VMARGS" ] && ERLANG_OPTS="$ERLANG_OPTS -args_file $VMARGS" 98 | ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump 99 | ERL_INETRC="$ETC_DIR"/inetrc 100 | 101 | # define ejabberd parameters 102 | EJABBERD_OPTS="\ 103 | $(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ 104 | $(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ 105 | $(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ 106 | $(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\ 107 | $EJABBERD_OPTS" 108 | [ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS" 109 | EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd" 110 | 111 | # export global variables 112 | export EJABBERD_CONFIG_PATH 113 | export EJABBERD_LOG_PATH 114 | export EJABBERD_PID_PATH 115 | export ERL_CRASH_DUMP 116 | export ERL_EPMD_ADDRESS 117 | export ERL_DIST_PORT 118 | export ERL_INETRC 119 | export ERL_MAX_PORTS 120 | export ERL_MAX_ETS_TABLES 121 | export CONTRIB_MODULES_PATH 122 | export CONTRIB_MODULES_CONF_DIR 123 | export ERL_LIBS 124 | 125 | set_dist_client() 126 | { 127 | [ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false" 128 | } 129 | 130 | # run command either directly or via su $INSTALLUSER 131 | run_cmd() 132 | { 133 | case $EXEC_CMD in 134 | as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; 135 | as_current_user) "$@" ;; 136 | esac 137 | } 138 | exec_cmd() 139 | { 140 | case $EXEC_CMD in 141 | as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;; 142 | as_current_user) exec "$@" ;; 143 | esac 144 | } 145 | run_erl() 146 | { 147 | NODE=$1; shift 148 | run_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" 149 | } 150 | exec_erl() 151 | { 152 | NODE=$1; shift 153 | exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@" 154 | } 155 | exec_iex() 156 | { 157 | NODE=$1; shift 158 | exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@" 159 | } 160 | 161 | # usage 162 | debugwarning() 163 | { 164 | if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then 165 | echo "--------------------------------------------------------------------" 166 | echo "" 167 | echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell" 168 | echo "to an already running ejabberd node." 169 | echo "If an ERROR is printed, it means the connection was not successful." 170 | echo "You can interact with the ejabberd node if you know how to use it." 171 | echo "Please be extremely cautious with your actions," 172 | echo "and exit immediately if you are not completely sure." 173 | echo "" 174 | echo "To exit and detach this shell from ejabberd, press:" 175 | echo " control+g and then q" 176 | echo "" 177 | #vt100 echo "Please do NOT use control+c in this debug shell !" 178 | #vt100 echo "" 179 | echo "--------------------------------------------------------------------" 180 | echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" 181 | echo " EJABBERD_BYPASS_WARNINGS=true" 182 | echo "Press return to continue" 183 | read -r _ 184 | echo "" 185 | fi 186 | } 187 | 188 | livewarning() 189 | { 190 | if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then 191 | echo "--------------------------------------------------------------------" 192 | echo "" 193 | echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode." 194 | echo "All log messages will be shown in the command shell." 195 | echo "You can interact with the ejabberd node if you know how to use it." 196 | echo "Please be extremely cautious with your actions," 197 | echo "and exit immediately if you are not completely sure." 198 | echo "" 199 | echo "To stop ejabberd gracefully:" 200 | echo " ejabberd:stop()." 201 | echo "To quit erlang immediately, press:" 202 | echo " control+g and then q" 203 | echo "" 204 | echo "--------------------------------------------------------------------" 205 | echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" 206 | echo " EJABBERD_BYPASS_WARNINGS=true" 207 | echo "Press return to continue" 208 | read -r _ 209 | echo "" 210 | fi 211 | } 212 | 213 | check_etop_result() 214 | { 215 | result=$? 216 | if [ $result -eq 1 ] ; then 217 | echo "" 218 | echo "It seems there was some problem running 'ejabberdctl etop'." 219 | echo "Is the error message something like this?" 220 | echo " Failed to load module 'etop' because it cannot be found..." 221 | echo "Then probably ejabberd was compiled with development tools disabled." 222 | echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools" 223 | echo "" 224 | exit $result 225 | fi 226 | } 227 | 228 | check_iex_result() 229 | { 230 | result=$? 231 | if [ $result -eq 127 ] ; then 232 | echo "" 233 | echo "It seems there was some problem finding 'iex' binary from Elixir." 234 | echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:" 235 | echo " ./configure" 236 | echo "which is equivalent to:" 237 | echo " ./configure --with-rebar=rebar3 --disable-elixir" 238 | echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:" 239 | echo " ./configure --enable-elixir" 240 | echo " ./configure --with-rebar=mix" 241 | echo "" 242 | exit $result 243 | fi 244 | } 245 | 246 | help() 247 | { 248 | echo "" 249 | echo "Commands to start an ejabberd node:" 250 | echo " start Start in server mode" 251 | echo " foreground Start in server mode (attached)" 252 | echo " foreground-quiet Start in server mode (attached), show only critical messages" 253 | echo " live Start in interactive mode, with Erlang shell" 254 | echo " iexlive Start in interactive mode, with Elixir shell" 255 | echo "" 256 | echo "Commands to interact with a running ejabberd node:" 257 | echo " debug Attach an interactive Erlang shell to a running node" 258 | echo " iexdebug Attach an interactive Elixir shell to a running node" 259 | echo " etop Attach to a running node and start Erlang Top" 260 | echo " ping Send ping to the node, returns pong or pang" 261 | echo " started|stopped Wait for the node to fully start|stop" 262 | echo "" 263 | echo "Optional parameters when starting an ejabberd node:" 264 | echo " --config-dir dir Config ejabberd: $ETC_DIR" 265 | echo " --config file Config ejabberd: $EJABBERD_CONFIG_PATH" 266 | echo " --ctl-config file Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH" 267 | echo " --logs dir Directory for logs: $LOGS_DIR" 268 | echo " --spool dir Database spool dir: $SPOOL_DIR" 269 | echo " --node nodename ejabberd node name: $ERLANG_NODE" 270 | echo "" 271 | } 272 | 273 | # dynamic node name helper 274 | uid() 275 | { 276 | uuid=$(uuidgen 2>/dev/null) 277 | [ -z "$uuid" ] && [ -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid) 278 | [ -z "$uuid" ] && uuid=$(printf "%X" "${RANDOM:-$$}$(date +%M%S)") 279 | uuid=$(printf '%s' $uuid | sed 's/^\(...\).*$/\1/') 280 | [ $# -eq 0 ] && echo "${uuid}-${ERLANG_NODE}" 281 | [ $# -eq 1 ] && echo "${uuid}-${1}-${ERLANG_NODE}" 282 | [ $# -eq 2 ] && echo "${uuid}-${1}@${2}" 283 | } 284 | 285 | # stop epmd if there is no other running node 286 | stop_epmd() 287 | { 288 | [ -n "$ERL_DIST_PORT" ] && return 289 | "$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null 290 | } 291 | 292 | # make sure node not already running and node name unregistered 293 | # if all ok, ensure runtime directory exists and make it current directory 294 | check_start() 295 | { 296 | ECSIMAGE_DBPATH=$HOME/database/$ERLANG_NODE 297 | [ ! -d "$ECSIMAGE_DBPATH" ] && ln -s $HOME/database $HOME/database/$ERLANG_NODE 298 | [ -n "$ERL_DIST_PORT" ] && return 299 | "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && { 300 | pgrep -f "$ERLANG_NODE" >/dev/null && { 301 | echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running." 302 | exit 4 303 | } 304 | pgrep beam >/dev/null && { 305 | echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered," 306 | echo " but no related beam process has been found." 307 | echo "Shutdown all other erlang nodes, and call 'epmd -kill'." 308 | exit 5 309 | } 310 | "$EPMD" -kill >/dev/null 311 | } 312 | } 313 | 314 | post_waiter_fork() 315 | { 316 | (FIRST_RUN=$FIRST_RUN "$0" post_waiter)& 317 | } 318 | 319 | post_waiter_waiting() 320 | { 321 | $0 started 322 | [ -n "$FIRST_RUN" ] && [ -n "$CTL_ON_CREATE" ] && (post_waiter_loop $CTL_ON_CREATE) 323 | [ -n "$CTL_ON_START" ] && post_waiter_loop $CTL_ON_START 324 | } 325 | 326 | post_waiter_loop() 327 | { 328 | LIST=$@ 329 | HEAD=${LIST%% ; *} 330 | TAIL=${LIST#* ; } 331 | HEAD2=${HEAD#\! *} 332 | echo ":> ejabberdctl $HEAD2" 333 | $0 $HEAD2 334 | ctlstatus=$? 335 | if [ $ctlstatus -ne 0 ] ; then 336 | if [ "$HEAD" != "$HEAD2" ] ; then 337 | echo ":> FAILURE in command '$HEAD2' !!! Ignoring result" 338 | else 339 | echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..." 340 | $0 halt > /dev/null 341 | exit $ctlstatus 342 | fi 343 | fi 344 | [ "$HEAD" = "$TAIL" ] || post_waiter_loop $TAIL 345 | } 346 | 347 | # allow sync calls 348 | wait_status() 349 | { 350 | # args: status try delay 351 | # return: 0 OK, 1 KO 352 | timeout="$2" 353 | status=4 354 | while [ "$status" -ne "$1" ] ; do 355 | sleep "$3" 356 | timeout=$((timeout - 1)) 357 | if [ $timeout -eq 0 ] ; then 358 | status="$1" 359 | else 360 | run_erl "$(uid ctl)" -hidden -noinput -s ejabberd_ctl -boot start_clean \ 361 | -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null 362 | status="$?" 363 | fi 364 | done 365 | [ $timeout -gt 0 ] 366 | } 367 | 368 | # ensure we can change current directory to SPOOL_DIR 369 | [ -d "$SPOOL_DIR" ] || FIRST_RUN=true 370 | [ -d "$SPOOL_DIR" ] || run_cmd mkdir -p "$SPOOL_DIR" 371 | cd "$SPOOL_DIR" || { 372 | echo "ERROR: can not access directory $SPOOL_DIR" 373 | exit 6 374 | } 375 | 376 | # main 377 | case $1 in 378 | start) 379 | check_start 380 | exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached 381 | ;; 382 | foreground) 383 | check_start 384 | post_waiter_fork 385 | exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput 386 | ;; 387 | foreground-quiet) 388 | check_start 389 | exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -ejabberd quiet true 390 | ;; 391 | live) 392 | livewarning 393 | check_start 394 | exec_erl "$ERLANG_NODE" $EJABBERD_OPTS 395 | ;; 396 | debug) 397 | debugwarning 398 | set_dist_client 399 | exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE" \ 400 | -boot start_clean 401 | ;; 402 | etop) 403 | set_dist_client 404 | exec_erl "$(uid top)" -hidden -node "$ERLANG_NODE" -s etop \ 405 | -s erlang halt -output text \ 406 | -boot start_clean 407 | ;; 408 | iexdebug) 409 | debugwarning 410 | set_dist_client 411 | exec_iex "$(uid debug)" --remsh "$ERLANG_NODE" --boot start_clean 412 | ;; 413 | iexlive) 414 | livewarning 415 | exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS" --app ejabberd 416 | ;; 417 | ping) 418 | PEER=${2:-$ERLANG_NODE} 419 | [ "$PEER" = "${PEER%.*}" ] && PS="-s" 420 | set_dist_client 421 | exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \ 422 | -noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \ 423 | -s erlang halt -output text \ 424 | -boot start_clean 425 | ;; 426 | started) 427 | set_dist_client 428 | wait_status 0 30 2 # wait 30x2s before timeout 429 | ;; 430 | stopped) 431 | set_dist_client 432 | wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout 433 | ;; 434 | post_waiter) 435 | post_waiter_waiting 436 | ;; 437 | *) 438 | set_dist_client 439 | run_erl "$(uid ctl)" -hidden -noinput -boot start_clean \ 440 | -s ejabberd_ctl -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" 441 | result=$? 442 | case $result in 443 | 2|3) help;; 444 | *) :;; 445 | esac 446 | exit $result 447 | ;; 448 | esac 449 | -------------------------------------------------------------------------------- /ecs/README.md: -------------------------------------------------------------------------------- 1 | 2 | [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&label=&color=49c0c4)](https://github.com/processone/ejabberd/tags) 3 | [![ejabberd Container on GitHub](https://img.shields.io/github/v/tag/processone/ejabberd?label=ejabberd&sort=semver&logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) 4 | [![ecs Container on Docker](https://img.shields.io/docker/v/ejabberd/ecs?label=ecs&sort=semver&logo=docker)](https://hub.docker.com/r/ejabberd/ecs/) 5 | 6 | ejabberd Container Images 7 | ========================= 8 | 9 | [ejabberd][home] is an open-source, 10 | robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang], 11 | that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service. 12 | 13 | [home]: https://www.ejabberd.im/ 14 | [erlang]: https://www.erlang.org/ 15 | [xmpp]: https://xmpp.org/ 16 | [mqtt]: https://mqtt.org/ 17 | [sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol 18 | 19 | This page documents those container images ([images comparison](#images-comparison)): 20 | 21 | - [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) 22 | published in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd), 23 | built using [ejabberd](https://github.com/processone/ejabberd/tree/master/.github/container) repository, 24 | both for stable ejabberd releases and the `master` branch, in x64 and arm64 architectures. 25 | 26 | - [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) 27 | published in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/), 28 | built using [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) repository 29 | for ejabberd stable releases in x64 architectures. 30 | 31 | For Microsoft Windows, see 32 | [Docker Desktop for Windows 10](https://www.process-one.net/blog/install-ejabberd-on-windows-10-using-docker-desktop/), 33 | and [Docker Toolbox for Windows 7](https://www.process-one.net/blog/install-ejabberd-on-windows-7-using-docker-toolbox/). 34 | 35 | For Kubernetes Helm, see [help-ejabberd](https://github.com/sando38/helm-ejabberd). 36 | 37 | 38 | Start ejabberd 39 | -------------- 40 | 41 | ### daemon 42 | 43 | Start ejabberd in a new container: 44 | 45 | ```bash 46 | docker run --name ejabberd -d -p 5222:5222 docker.io/ejabberd/ecs 47 | ``` 48 | 49 | That runs the container as a daemon, 50 | using ejabberd default configuration file and XMPP domain `localhost`. 51 | 52 | Restart the stopped ejabberd container: 53 | 54 | ```bash 55 | docker restart ejabberd 56 | ``` 57 | 58 | Stop the running container: 59 | 60 | ```bash 61 | docker stop ejabberd 62 | ``` 63 | 64 | Remove the ejabberd container: 65 | 66 | ```bash 67 | docker rm ejabberd 68 | ``` 69 | 70 | 71 | ### with Erlang console 72 | 73 | Start ejabberd with an interactive Erlang console attached using the `live` command: 74 | 75 | ```bash 76 | docker run --name ejabberd -it -p 5222:5222 docker.io/ejabberd/ecs live 77 | ``` 78 | 79 | That uses the default configuration file and XMPP domain `localhost`. 80 | 81 | 82 | ### with your data 83 | 84 | Pass a configuration file as a volume 85 | and share the local directory to store database: 86 | 87 | ```bash 88 | mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml 89 | 90 | mkdir database && chown ejabberd database 91 | 92 | docker run --name ejabberd -it \ 93 | -v $(pwd)/conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \ 94 | -v $(pwd)/database:/opt/ejabberd/database \ 95 | -p 5222:5222 docker.io/ejabberd/ecs live 96 | ``` 97 | 98 | Notice that ejabberd runs in the container with an account named `ejabberd` 99 | with UID 9000 and group `ejabberd` with GID 9000, 100 | and the volumes you mount must grant proper rights to that account. 101 | 102 | 103 | Next steps 104 | ---------- 105 | 106 | ### Register admin account 107 | 108 | #### [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) [:orange_circle:](#images-comparison) 109 | 110 | If you set the `REGISTER_ADMIN_PASSWORD` environment variable, 111 | an account is automatically registered with that password, 112 | and admin privileges are granted to it. 113 | The account created depends on what variables you have set: 114 | 115 | - `EJABBERD_MACRO_ADMIN=juliet@example.org` -> `juliet@example.org` 116 | - `EJABBERD_MACRO_HOST=example.org` -> `admin@example.org` 117 | - None of those variables are set -> `admin@localhost` 118 | 119 | The account registration is shown in the container log: 120 | 121 | ``` 122 | :> ejabberdctl register admin example.org somePassw0rd 123 | User admin@example.org successfully registered 124 | ``` 125 | 126 | Alternatively, you can register the account manually yourself 127 | and edit `conf/ejabberd.yml` and add the ACL as explained in 128 | [ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account). 129 | 130 | --- 131 | 132 | #### [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) 133 | 134 | The default ejabberd configuration has already granted admin privilege 135 | to an account that would be called `admin@localhost`, 136 | so you just need to register it, for example: 137 | 138 | ```bash 139 | docker exec -it ejabberd ejabberdctl register admin localhost passw0rd 140 | ``` 141 | 142 | ### Check ejabberd log 143 | 144 | Check the content of the log files inside the container, 145 | even if you do not put it on a shared persistent drive: 146 | 147 | ```bash 148 | docker exec -it ejabberd tail -f logs/ejabberd.log 149 | ``` 150 | 151 | 152 | ### Inspect container files 153 | 154 | The container uses Alpine Linux. Start a shell inside the container: 155 | 156 | ```bash 157 | docker exec -it ejabberd sh 158 | ``` 159 | 160 | 161 | ### Open debug console 162 | 163 | Open an interactive debug Erlang console attached to a running ejabberd in a running container: 164 | 165 | ```bash 166 | docker exec -it ejabberd ejabberdctl debug 167 | ``` 168 | 169 | 170 | ### CAPTCHA 171 | 172 | ejabberd includes two example CAPTCHA scripts. 173 | If you want to use any of them, first install some additional required libraries: 174 | 175 | ```bash 176 | docker exec --user root ejabberd apk add imagemagick ghostscript-fonts bash 177 | ``` 178 | 179 | Now update your ejabberd configuration file, for example: 180 | ```bash 181 | docker exec -it ejabberd vi conf/ejabberd.yml 182 | ``` 183 | 184 | and add this option: 185 | ```yaml 186 | captcha_cmd: "$HOME/bin/captcha.sh" 187 | ``` 188 | 189 | Finally, reload the configuration file or restart the container: 190 | ```bash 191 | docker exec ejabberd ejabberdctl reload_config 192 | ``` 193 | 194 | If the CAPTCHA image is not visible, there may be a problem generating it 195 | (the ejabberd log file may show some error message); 196 | or the image URL may not be correctly detected by ejabberd, 197 | in that case you can set the correct URL manually, for example: 198 | ```yaml 199 | captcha_url: https://localhost:5443/captcha 200 | ``` 201 | 202 | For more details about CAPTCHA options, please check the 203 | [CAPTCHA](https://docs.ejabberd.im/admin/configuration/basic/#captcha) 204 | documentation section. 205 | 206 | 207 | Advanced 208 | -------- 209 | 210 | ### Ports 211 | 212 | The container image exposes several ports 213 | (check also [Docs: Firewall Settings](https://docs.ejabberd.im/admin/guide/security/#firewall-settings)): 214 | 215 | - `5222`: The default port for XMPP clients. 216 | - `5269`: For XMPP federation. Only needed if you want to communicate with users on other servers. 217 | - `5280`: For admin interface (URL is `admin/`). 218 | - `1880`: For admin interface (URL is `/`, useful for [podman-desktop](https://podman-desktop.io/) and [docker-desktop](https://www.docker.com/products/docker-desktop/)) [:orange_circle:](#images-comparison) 219 | - `5443`: With encryption, used for admin interface, API, CAPTCHA, OAuth, Websockets and XMPP BOSH. 220 | - `1883`: Used for MQTT 221 | - `4369-4399`: EPMD and Erlang connectivity, used for `ejabberdctl` and clustering 222 | - `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD [:orange_circle:](#images-comparison) 223 | 224 | 225 | ### Volumes 226 | 227 | ejabberd produces two types of data: log files and database spool files (Mnesia). 228 | This is the kind of data you probably want to store on a persistent or local drive (at least the database). 229 | 230 | The volumes you may want to map: 231 | 232 | - `/opt/ejabberd/conf/`: Directory containing configuration and certificates 233 | - `/opt/ejabberd/database/`: Directory containing Mnesia database. 234 | You should back up or export the content of the directory to persistent storage 235 | (host storage, local storage, any storage plugin) 236 | - `/opt/ejabberd/logs/`: Directory containing log files 237 | - `/opt/ejabberd/upload/`: Directory containing uploaded files. This should also be backed up. 238 | 239 | All these files are owned by an account named `ejabberd` with group `ejabberd` in the container. 240 | Its corresponding `UID:GID` is `9000:9000`. 241 | If you prefer bind mounts instead of volumes, then 242 | you need to map this to valid `UID:GID` on your host to get read/write access on 243 | mounted directories. 244 | 245 | If using Docker, try: 246 | ```bash 247 | mkdir database 248 | sudo chown 9000:9000 database 249 | ``` 250 | 251 | If using Podman, try: 252 | ```bash 253 | mkdir database 254 | podman unshare chown 9000:9000 database 255 | ``` 256 | 257 | It's possible to install additional ejabberd modules using volumes, check 258 | [this Docs tutorial](https://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container). 259 | 260 | 261 | ### Commands on start 262 | 263 | The ejabberdctl script reads the `CTL_ON_CREATE` environment variable 264 | the first time the container is started, 265 | and reads `CTL_ON_START` every time the container is started. 266 | Those variables can contain one ejabberdctl command, 267 | or several commands separated with the blankspace and `;` characters. 268 | 269 | If any of those commands returns a failure, the container starting gets aborted. 270 | If there is a command with a result that can be ignored, 271 | prefix that command with `!` 272 | 273 | This example, registers an `admin@localhost` account when the container is first created. 274 | Everytime the container starts, it shows the list of registered accounts, 275 | checks that the admin account exists and password is valid, 276 | changes the password of an account if it exists (ignoring any failure), 277 | and shows the ejabberd starts (check also the [full example](#customized-example)): 278 | ```yaml 279 | environment: 280 | - CTL_ON_CREATE=register admin localhost asd 281 | - CTL_ON_START=stats registeredusers ; 282 | check_password admin localhost asd ; 283 | ! change_password bot123 localhost qqq ; 284 | status 285 | ``` 286 | 287 | 288 | ### Macros in environment [:high_brightness:](#images-comparison) 289 | 290 | ejabberd reads `EJABBERD_MACRO_*` environment variables 291 | and uses them to define the corresponding 292 | [macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file), 293 | overwriting the corresponding macro definition if it was set in the configuration file. 294 | This is supported since ejabberd 24.12. 295 | 296 | For example, if you configure this in `ejabberd.yml`: 297 | 298 | ```yaml 299 | acl: 300 | admin: 301 | user: ADMIN 302 | ``` 303 | 304 | now you can define the admin account JID using an environment variable: 305 | ```yaml 306 | environment: 307 | - EJABBERD_MACRO_ADMIN=admin@localhost 308 | ``` 309 | 310 | Check the [full example](#customized-example) for other example. 311 | 312 | 313 | ### ejabberd-contrib 314 | 315 | This section addresses those topics related to 316 | [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib): 317 | 318 | - [Download source code](#download-source-code) 319 | - [Install a module](#install-a-module) 320 | - [Install git for dependencies](#install-git-for-dependencies) 321 | - [Install your module](#install-your-module) 322 | 323 | --- 324 | 325 | #### Download source code 326 | 327 | The `ejabberd` container image includes the ejabberd-contrib git repository source code, 328 | but `ecs` does not, so first download it: 329 | ```bash 330 | $ docker exec ejabberd ejabberdctl modules_update_specs 331 | ``` 332 | 333 | #### Install a module 334 | 335 | Compile and install any of the contributed modules, for example: 336 | ```bash 337 | docker exec ejabberd ejabberdctl module_install mod_statsdx 338 | 339 | Module mod_statsdx has been installed and started. 340 | It's configured in the file: 341 | /opt/ejabberd/.ejabberd-modules/mod_statsdx/conf/mod_statsdx.yml 342 | Configure the module in that file, or remove it 343 | and configure in your main ejabberd.yml 344 | ``` 345 | 346 | #### Install git for dependencies 347 | 348 | Some modules depend on erlang libraries, 349 | but the container images do not include `git` or `mix` to download them. 350 | Consequently, when you attempt to install such a module, 351 | there will be error messages like: 352 | 353 | ```bash 354 | docker exec ejabberd ejabberdctl module_install ejabberd_observer_cli 355 | 356 | I'll download "recon" using git because I can't use Mix to fetch from hex.pm: 357 | /bin/sh: mix: not found 358 | Fetching dependency observer_cli: 359 | /bin/sh: git: not found 360 | ... 361 | ``` 362 | 363 | the solution is to install `git` in the container image: 364 | 365 | ```bash 366 | docker exec --user root ejabberd apk add git 367 | 368 | fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz 369 | fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz 370 | (1/3) Installing pcre2 (10.43-r0) 371 | (2/3) Installing git (2.47.2-r0) 372 | (3/3) Installing git-init-template (2.47.2-r0) 373 | Executing busybox-1.37.0-r12.trigger 374 | OK: 27 MiB in 42 packages 375 | ``` 376 | 377 | and now you can upgrade the module: 378 | 379 | ```bash 380 | docker exec ejabberd ejabberdctl module_upgrade ejabberd_observer_cli 381 | 382 | I'll download "recon" using git because I can't use Mix to fetch from hex.pm: 383 | /bin/sh: mix: not found 384 | Fetching dependency observer_cli: Cloning into 'observer_cli'... 385 | Fetching dependency os_stats: Cloning into 'os_stats'... 386 | Fetching dependency recon: Cloning into 'recon'... 387 | Inlining: inline_size=24 inline_effort=150 388 | Old inliner: threshold=0 functions=[{insert,2},{merge,2}] 389 | Module ejabberd_observer_cli has been installed. 390 | Now you can configure it in your ejabberd.yml 391 | I'll download "recon" using git because I can't use Mix to fetch from hex.pm: 392 | /bin/sh: mix: not found 393 | ``` 394 | 395 | #### Install your module 396 | 397 | If you [developed an ejabberd module](https://docs.ejabberd.im/developer/extending-ejabberd/modules/), 398 | you can install it in your container image: 399 | 400 | 1. Create a local directory for `ejabberd-modules`: 401 | 402 | ``` sh 403 | mkdir docker-modules 404 | ``` 405 | 406 | 2. Then create the directory structure for your custom module: 407 | 408 | ``` sh 409 | cd docker-modules 410 | 411 | mkdir -p sources/mod_hello_world/ 412 | touch sources/mod_hello_world/mod_hello_world.spec 413 | 414 | mkdir sources/mod_hello_world/src/ 415 | mv mod_hello_world.erl sources/mod_hello_world/src/ 416 | 417 | mkdir sources/mod_hello_world/conf/ 418 | echo -e "modules:\n mod_hello_world: {}" > sources/mod_hello_world/conf/mod_hello_world.yml 419 | 420 | cd .. 421 | ``` 422 | 423 | 3. Grant ownership of that directory to the UID that ejabberd will use inside the Docker image: 424 | 425 | ``` sh 426 | sudo chown 9000 -R docker-modules/ 427 | ``` 428 | 429 | 4. Start ejabberd in the container: 430 | 431 | ``` sh 432 | sudo docker run \ 433 | --name hellotest \ 434 | -d \ 435 | --volume "$(pwd)/docker-modules:/home/ejabberd/.ejabberd-modules/" \ 436 | -p 5222:5222 \ 437 | -p 5280:5280 \ 438 | ejabberd/ecs 439 | ``` 440 | 441 | 5. Check the module is available for installing, and then install it: 442 | 443 | ``` sh 444 | sudo docker exec -it hellotest ejabberdctl modules_available 445 | mod_hello_world [] 446 | 447 | sudo docker exec -it hellotest ejabberdctl module_install mod_hello_world 448 | ``` 449 | 450 | 6. If the module works correctly, you will see `Hello` in the ejabberd logs when it starts: 451 | 452 | ``` sh 453 | sudo docker exec -it hellotest grep Hello logs/ejabberd.log 454 | 2020-10-06 13:40:13.154335+00:00 [info] 455 | <0.492.0>@mod_hello_world:start/2:15 Hello, ejabberd world! 456 | ``` 457 | 458 | 459 | ### ejabberdapi 460 | 461 | When the container is running (and thus ejabberd), you can exec commands inside the container 462 | using `ejabberdctl` or any other of the available interfaces, see 463 | [Understanding ejabberd "commands"](https://docs.ejabberd.im/developer/ejabberd-api/#understanding-ejabberd-commands) 464 | 465 | Additionally, the container image includes the `ejabberdapi` executable. 466 | Please check the [ejabberd-api homepage](https://github.com/processone/ejabberd-api) 467 | for configuration and usage details. 468 | 469 | For example, if you configure ejabberd like this: 470 | ```yaml 471 | listen: 472 | - 473 | port: 5282 474 | module: ejabberd_http 475 | request_handlers: 476 | "/api": mod_http_api 477 | 478 | acl: 479 | loopback: 480 | ip: 481 | - 127.0.0.0/8 482 | - ::1/128 483 | - ::FFFF:127.0.0.1/128 484 | 485 | api_permissions: 486 | "admin access": 487 | who: 488 | access: 489 | allow: 490 | acl: loopback 491 | what: 492 | - "register" 493 | ``` 494 | 495 | Then you could register new accounts with this query: 496 | 497 | ```bash 498 | docker exec -it ejabberd ejabberdapi register --endpoint=http://127.0.0.1:5282/ --jid=admin@localhost --password=passw0rd 499 | ``` 500 | 501 | 502 | ### Clustering 503 | 504 | When setting several containers to form a 505 | [cluster of ejabberd nodes](https://docs.ejabberd.im/admin/guide/clustering/), 506 | each one must have a different 507 | [Erlang Node Name](https://docs.ejabberd.im/admin/guide/security/#erlang-node-name) 508 | and the same 509 | [Erlang Cookie](https://docs.ejabberd.im/admin/guide/security/#erlang-cookie). 510 | 511 | For this you can either: 512 | 513 | - edit `conf/ejabberdctl.cfg` and set variables `ERLANG_NODE` and `ERLANG_COOKIE` 514 | - set the environment variables `ERLANG_NODE_ARG` and `ERLANG_COOKIE` 515 | 516 | --- 517 | 518 | Example to connect a local `ejabberdctl` to a containerized ejabberd: 519 | 520 | 1. When creating the container, export port 5210, and set `ERLANG_COOKIE`: 521 | ```sh 522 | docker run --name ejabberd -it \ 523 | -e ERLANG_COOKIE=`cat $HOME/.erlang.cookie` \ 524 | -p 5210:5210 -p 5222:5222 \ 525 | docker.io/ejabberd/ecs 526 | ``` 527 | 2. Set `ERL_DIST_PORT=5210` in `ejabberdctl.cfg` of container and local ejabberd 528 | 3. Restart the container 529 | 4. Now use `ejabberdctl` in your local ejabberd deployment 530 | 531 | To connect using a local `ejabberd` script: 532 | ```sh 533 | ERL_DIST_PORT=5210 _build/dev/rel/ejabberd/bin/ejabberd ping 534 | ``` 535 | 536 | Example using environment variables (see full example [docker-compose.yml](https://github.com/processone/docker-ejabberd/issues/64#issuecomment-887741332)): 537 | ```yaml 538 | environment: 539 | - ERLANG_NODE_ARG=ejabberd@node7 540 | - ERLANG_COOKIE=dummycookie123 541 | ``` 542 | 543 | --- 544 | 545 | Once you have the ejabberd nodes properly set and running, 546 | you can tell the secondary nodes to join the master node using the 547 | [`join_cluster`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join-cluster) 548 | API call. 549 | 550 | Example using environment variables (see the full 551 | [`docker-compose.yml` clustering example](#clustering-example)): 552 | ```yaml 553 | environment: 554 | - ERLANG_NODE_ARG=ejabberd@replica 555 | - ERLANG_COOKIE=dummycookie123 556 | - CTL_ON_CREATE=join_cluster ejabberd@main 557 | ``` 558 | 559 | ### Change Mnesia Node Name 560 | 561 | To use the same Mnesia database in a container with a different hostname, 562 | it is necessary to change the old hostname stored in Mnesia. 563 | 564 | This section is equivalent to the ejabberd Documentation 565 | [Change Computer Hostname](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname), 566 | but particularized to containers that use this 567 | `ecs` container image from ejabberd 23.01 or older. 568 | 569 | #### Setup Old Container 570 | 571 | Let's assume a container running ejabberd 23.01 (or older) from 572 | this `ecs` container image, with the database directory binded 573 | and one registered account. 574 | This can be produced with: 575 | ```bash 576 | OLDCONTAINER=ejaold 577 | NEWCONTAINER=ejanew 578 | 579 | mkdir database 580 | sudo chown 9000:9000 database 581 | docker run -d --name $OLDCONTAINER -p 5222:5222 \ 582 | -v $(pwd)/database:/opt/ejabberd/database \ 583 | docker.io/ejabberd/ecs:23.01 584 | docker exec -it $OLDCONTAINER ejabberdctl started 585 | docker exec -it $OLDCONTAINER ejabberdctl register user1 localhost somepass 586 | docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost 587 | ``` 588 | 589 | Methods to know the Erlang node name: 590 | ```bash 591 | ls database/ | grep ejabberd@ 592 | docker exec -it $OLDCONTAINER ejabberdctl status 593 | docker exec -it $OLDCONTAINER grep "started in the node" logs/ejabberd.log 594 | ``` 595 | 596 | #### Change Mnesia Node 597 | 598 | First of all let's store the Erlang node names and paths in variables. 599 | In this example they would be: 600 | ```bash 601 | OLDCONTAINER=ejaold 602 | NEWCONTAINER=ejanew 603 | OLDNODE=ejabberd@95145ddee27c 604 | NEWNODE=ejabberd@localhost 605 | OLDFILE=/opt/ejabberd/database/old.backup 606 | NEWFILE=/opt/ejabberd/database/new.backup 607 | ``` 608 | 609 | 1. Start your old container that can still read the Mnesia database correctly. 610 | If you have the Mnesia spool files, 611 | but don't have access to the old container anymore, go to 612 | [Create Temporary Container](#create-temporary-container) 613 | and later come back here. 614 | 615 | 2. Generate a backup file and check it was created: 616 | ```bash 617 | docker exec -it $OLDCONTAINER ejabberdctl backup $OLDFILE 618 | ls -l database/*.backup 619 | ``` 620 | 621 | 3. Stop ejabberd: 622 | ```bash 623 | docker stop $OLDCONTAINER 624 | ``` 625 | 626 | 4. Create the new container. For example: 627 | ```bash 628 | docker run \ 629 | --name $NEWCONTAINER \ 630 | -d \ 631 | -p 5222:5222 \ 632 | -v $(pwd)/database:/opt/ejabberd/database \ 633 | docker.io/ejabberd/ecs:latest 634 | ``` 635 | 636 | 5. Convert the backup file to new node name: 637 | ```bash 638 | docker exec -it $NEWCONTAINER ejabberdctl mnesia_change_nodename $OLDNODE $NEWNODE $OLDFILE $NEWFILE 639 | ``` 640 | 641 | 6. Install the backup file as a fallback: 642 | ```bash 643 | docker exec -it $NEWCONTAINER ejabberdctl install_fallback $NEWFILE 644 | ``` 645 | 646 | 7. Restart the container: 647 | ```bash 648 | docker restart $NEWCONTAINER 649 | ``` 650 | 651 | 8. Check that the information of the old database is available. 652 | In this example, it should show that the account `user1` is registered: 653 | ```bash 654 | docker exec -it $NEWCONTAINER ejabberdctl registered_users localhost 655 | ``` 656 | 657 | 9. When the new container is working perfectly with the converted Mnesia database, 658 | you may want to remove the unneeded files: 659 | the old container, the old Mnesia spool files, and the backup files. 660 | 661 | #### Create Temporary Container 662 | 663 | In case the old container that used the Mnesia database is not available anymore, 664 | a temporary container can be created just to read the Mnesia database 665 | and make a backup of it, as explained in the previous section. 666 | 667 | This method uses `--hostname` command line argument for docker, 668 | and `ERLANG_NODE_ARG` environment variable for ejabberd. 669 | Their values must be the hostname of your old container 670 | and the Erlang node name of your old ejabberd node. 671 | To know the Erlang node name please check 672 | [Setup Old Container](#setup-old-container). 673 | 674 | Command line example: 675 | ```bash 676 | OLDHOST=${OLDNODE#*@} 677 | docker run \ 678 | -d \ 679 | --name $OLDCONTAINER \ 680 | --hostname $OLDHOST \ 681 | -p 5222:5222 \ 682 | -v $(pwd)/database:/opt/ejabberd/database \ 683 | -e ERLANG_NODE_ARG=$OLDNODE \ 684 | docker.io/ejabberd/ecs:latest 685 | ``` 686 | 687 | Check the old database content is available: 688 | ```bash 689 | docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost 690 | ``` 691 | 692 | Now that you have ejabberd running with access to the Mnesia database, 693 | you can continue with step 2 of previous section 694 | [Change Mnesia Node](#change-mnesia-node). 695 | 696 | 697 | Build Container Image 698 | ---------------- 699 | 700 | The container image includes ejabberd as a standalone OTP release built using Elixir. 701 | 702 | ### Build `ejabberd` [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) 703 | 704 | The ejabberd Erlang/OTP release is configured with: 705 | 706 | - `mix.exs`: Customize ejabberd release 707 | - `vars.config`: ejabberd compilation configuration options 708 | - `config/runtime.exs`: Customize ejabberd paths 709 | - `ejabberd.yml.template`: ejabberd default config file 710 | 711 | #### Direct build 712 | 713 | Build ejabberd Community Server container image from ejabberd master git repository: 714 | 715 | ```bash 716 | docker buildx build \ 717 | -t personal/ejabberd \ 718 | -f .github/container/Dockerfile \ 719 | . 720 | ``` 721 | 722 | #### Podman build 723 | 724 | To build the image using Podman, please notice: 725 | 726 | - `EXPOSE 4369-4399` port range is not supported, remove that in Dockerfile 727 | - It mentions that `healthcheck` is not supported by the Open Container Initiative image format 728 | - to start with command `live`, you may want to add environment variable `EJABBERD_BYPASS_WARNINGS=true` 729 | 730 | ```bash 731 | podman build \ 732 | -t ejabberd \ 733 | -f .github/container/Dockerfile \ 734 | . 735 | 736 | podman run --name eja1 -d -p 5222:5222 localhost/ejabberd 737 | 738 | podman exec eja1 ejabberdctl status 739 | 740 | podman exec -it eja1 sh 741 | 742 | podman stop eja1 743 | 744 | podman run --name eja1 -it -e EJABBERD_BYPASS_WARNINGS=true -p 5222:5222 localhost/ejabberd live 745 | ``` 746 | 747 | ### Build `ecs` [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) 748 | 749 | The ejabberd Erlang/OTP release is configured with: 750 | 751 | - `rel/config.exs`: Customize ejabberd release 752 | - `rel/dev.exs`: ejabberd environment configuration for development release 753 | - `rel/prod.exs`: ejabberd environment configuration for production release 754 | - `vars.config`: ejabberd compilation configuration options 755 | - `conf/ejabberd.yml`: ejabberd default config file 756 | 757 | Build ejabberd Community Server base image from ejabberd master on Github: 758 | 759 | ```bash 760 | docker build -t personal/ejabberd . 761 | ``` 762 | 763 | Build ejabberd Community Server base image for a given ejabberd version: 764 | 765 | ```bash 766 | ./build.sh 18.03 767 | ``` 768 | 769 | Composer Examples 770 | ----------------- 771 | 772 | ### Minimal Example 773 | 774 | This is the barely minimal file to get a usable ejabberd. 775 | 776 | If using Docker, write this `docker-compose.yml` file 777 | and start it with `docker-compose up`: 778 | 779 | ```yaml 780 | services: 781 | main: 782 | image: docker.io/ejabberd/ecs 783 | container_name: ejabberd 784 | ports: 785 | - "5222:5222" 786 | - "5269:5269" 787 | - "5280:5280" 788 | - "5443:5443" 789 | ``` 790 | 791 | If using Podman, write this `minimal.yml` file 792 | and start it with `podman kube play minimal.yml`: 793 | 794 | ```yaml 795 | apiVersion: v1 796 | 797 | kind: Pod 798 | 799 | metadata: 800 | name: ejabberd 801 | 802 | spec: 803 | containers: 804 | 805 | - name: ejabberd 806 | image: docker.io/ejabberd/ecs 807 | ports: 808 | - containerPort: 5222 809 | hostPort: 5222 810 | - containerPort: 5269 811 | hostPort: 5269 812 | - containerPort: 5280 813 | hostPort: 5280 814 | - containerPort: 5443 815 | hostPort: 5443 816 | ``` 817 | 818 | 819 | ### Customized Example 820 | 821 | This example shows the usage of several customizations: 822 | it uses a local configuration file, 823 | defines a configuration macro using an environment variable, 824 | stores the mnesia database in a local path, 825 | registers an account when it's created, 826 | and checks the number of registered accounts every time it's started. 827 | 828 | Prepare an ejabberd configuration file: 829 | ```bash 830 | mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml 831 | ``` 832 | 833 | Create the database directory and allow the container access to it: 834 | 835 | - Docker: 836 | ```bash 837 | mkdir database && sudo chown 9000:9000 database 838 | ``` 839 | - Podman: 840 | ```bash 841 | mkdir database && podman unshare chown 9000:9000 database 842 | ``` 843 | 844 | If using Docker, write this `docker-compose.yml` file 845 | and start it with `docker-compose up`: 846 | 847 | ```yaml 848 | version: '3.7' 849 | 850 | services: 851 | 852 | main: 853 | image: docker.io/ejabberd/ecs 854 | container_name: ejabberd 855 | environment: 856 | - EJABBERD_MACRO_HOST=example.com 857 | - EJABBERD_MACRO_ADMIN=admin@example.com 858 | - REGISTER_ADMIN_PASSWORD=somePassw0rd 859 | - CTL_ON_START=registered_users example.com ; 860 | status 861 | ports: 862 | - "5222:5222" 863 | - "5269:5269" 864 | - "5280:5280" 865 | - "5443:5443" 866 | volumes: 867 | - ./conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro 868 | - ./database:/opt/ejabberd/database 869 | ``` 870 | 871 | If using Podman, write this `custom.yml` file 872 | and start it with `podman kube play custom.yml`: 873 | 874 | ```yaml 875 | apiVersion: v1 876 | 877 | kind: Pod 878 | 879 | metadata: 880 | name: ejabberd 881 | 882 | spec: 883 | containers: 884 | 885 | - name: ejabberd 886 | image: docker.io/ejabberd/ecs 887 | env: 888 | - name: EJABBERD_MACRO_HOST 889 | value: example.com 890 | - name: EJABBERD_MACRO_ADMIN 891 | value: admin@example.com 892 | - name: REGISTER_ADMIN_PASSWORD 893 | value: somePassw0rd 894 | - name: CTL_ON_START 895 | value: registered_users example.com ; 896 | status 897 | ports: 898 | - containerPort: 5222 899 | hostPort: 5222 900 | - containerPort: 5269 901 | hostPort: 5269 902 | - containerPort: 5280 903 | hostPort: 5280 904 | - containerPort: 5443 905 | hostPort: 5443 906 | volumeMounts: 907 | - mountPath: /opt/ejabberd/conf/ejabberd.yml 908 | name: config 909 | readOnly: true 910 | - mountPath: /opt/ejabberd/database 911 | name: db 912 | 913 | volumes: 914 | - name: config 915 | hostPath: 916 | path: ./conf/ejabberd.yml 917 | type: File 918 | - name: db 919 | hostPath: 920 | path: ./database 921 | type: DirectoryOrCreate 922 | ``` 923 | 924 | 925 | ### Clustering Example 926 | 927 | In this example, the main container is created first. 928 | Once it is fully started and healthy, a second container is created, 929 | and once ejabberd is started in it, it joins the first one. 930 | 931 | An account is registered in the first node when created (and 932 | we ignore errors that can happen when doing that - for example 933 | when account already exists), 934 | and it should exist in the second node after join. 935 | 936 | Notice that in this example the main container does not have access 937 | to the exterior; the replica exports the ports and can be accessed. 938 | 939 | If using Docker, write this `docker-compose.yml` file 940 | and start it with `docker-compose up`: 941 | 942 | ```yaml 943 | version: '3.7' 944 | 945 | services: 946 | 947 | main: 948 | image: docker.io/ejabberd/ecs 949 | container_name: main 950 | environment: 951 | - ERLANG_NODE_ARG=ejabberd@main 952 | - ERLANG_COOKIE=dummycookie123 953 | - CTL_ON_CREATE=! register admin localhost asd 954 | healthcheck: 955 | test: netstat -nl | grep -q 5222 956 | start_period: 5s 957 | interval: 5s 958 | timeout: 5s 959 | retries: 120 960 | 961 | replica: 962 | image: docker.io/ejabberd/ecs 963 | container_name: replica 964 | depends_on: 965 | main: 966 | condition: service_healthy 967 | environment: 968 | - ERLANG_NODE_ARG=ejabberd@replica 969 | - ERLANG_COOKIE=dummycookie123 970 | - CTL_ON_CREATE=join_cluster ejabberd@main 971 | - CTL_ON_START=registered_users localhost ; 972 | status 973 | ports: 974 | - "5222:5222" 975 | - "5269:5269" 976 | - "5280:5280" 977 | - "5443:5443" 978 | ``` 979 | 980 | If using Podman, write this `cluster.yml` file 981 | and start it with `podman kube play cluster.yml`: 982 | 983 | ```yaml 984 | apiVersion: v1 985 | 986 | kind: Pod 987 | 988 | metadata: 989 | name: cluster 990 | 991 | spec: 992 | containers: 993 | 994 | - name: first 995 | image: docker.io/ejabberd/ecs 996 | env: 997 | - name: ERLANG_NODE_ARG 998 | value: main@cluster 999 | - name: ERLANG_COOKIE 1000 | value: dummycookie123 1001 | - name: CTL_ON_CREATE 1002 | value: register admin localhost asd 1003 | - name: CTL_ON_START 1004 | value: stats registeredusers ; 1005 | status 1006 | - name: EJABBERD_MACRO_PORT_C2S 1007 | value: 6222 1008 | - name: EJABBERD_MACRO_PORT_C2S_TLS 1009 | value: 6223 1010 | - name: EJABBERD_MACRO_PORT_S2S 1011 | value: 6269 1012 | - name: EJABBERD_MACRO_PORT_HTTP_TLS 1013 | value: 6443 1014 | - name: EJABBERD_MACRO_PORT_HTTP 1015 | value: 6280 1016 | - name: EJABBERD_MACRO_PORT_MQTT 1017 | value: 6883 1018 | - name: EJABBERD_MACRO_PORT_PROXY65 1019 | value: 6777 1020 | volumeMounts: 1021 | - mountPath: /opt/ejabberd/conf/ejabberd.yml 1022 | name: config 1023 | readOnly: true 1024 | 1025 | - name: second 1026 | image: docker.io/ejabberd/ecs 1027 | env: 1028 | - name: ERLANG_NODE_ARG 1029 | value: replica@cluster 1030 | - name: ERLANG_COOKIE 1031 | value: dummycookie123 1032 | - name: CTL_ON_CREATE 1033 | value: join_cluster main@cluster ; 1034 | started ; 1035 | list_cluster 1036 | - name: CTL_ON_START 1037 | value: stats registeredusers ; 1038 | check_password admin localhost asd ; 1039 | status 1040 | ports: 1041 | - containerPort: 5222 1042 | hostPort: 5222 1043 | - containerPort: 5280 1044 | hostPort: 5280 1045 | volumeMounts: 1046 | - mountPath: /opt/ejabberd/conf/ejabberd.yml 1047 | name: config 1048 | readOnly: true 1049 | 1050 | volumes: 1051 | - name: config 1052 | hostPath: 1053 | path: ./conf/ejabberd.yml 1054 | type: File 1055 | 1056 | ``` 1057 | 1058 | 1059 | Images Comparison 1060 | ----------------- 1061 | 1062 | Let's summarize the differences between both container images. Legend: 1063 | 1064 | - :sparkle: is the recommended alternative 1065 | - :orange_circle: added in the latest release (ejabberd 25.03) 1066 | - :high_brightness: added in the previous release (ejabberd 24.12) 1067 | - :low_brightness: added in the pre-previous release (ejabberd 24.10) 1068 | 1069 | | | [![ejabberd Container](https://img.shields.io/badge/ejabberd-grey?logo=opencontainersinitiative&logoColor=2094f3)](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [![ecs Container](https://img.shields.io/badge/ecs-grey?logo=docker&logoColor=2094f3)](https://hub.docker.com/r/ejabberd/ecs/) | 1070 | |:----------------------|:------------------|:-----------------------| 1071 | | Source code | [ejabberd/.github/container](https://github.com/processone/ejabberd/tree/master/.github/container) | [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) | 1072 | | Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) | 1073 | | Built for | stable releases
`master` branch | stable releases
[`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) | 1074 | | Architectures | `linux/amd64`
`linux/arm64` | `linux/amd64` | 1075 | | Software | Erlang/OTP 27.3.4.3-alpine
Elixir 1.18.4 | Alpine 3.22
Erlang/OTP 26.2
Elixir 1.18.3 | 1076 | | Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/)
[ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) | 1077 | | :black_square_button: **Additional content** | 1078 | | [ejabberd-contrib](#ejabberd-contrib) | included | not included | 1079 | | [ejabberdapi](#ejabberdapi) | included :orange_circle: | included | 1080 | | :black_square_button: **Ports** | 1081 | | [1880](#ports) for WebAdmin | yes :orange_circle: | yes :orange_circle: | 1082 | | [5210](#ports) for `ERL_DIST_PORT` | supported | supported :orange_circle: | 1083 | | :black_square_button: **Paths** | 1084 | | `$HOME` | `/opt/ejabberd/` | `/home/ejabberd/` | 1085 | | User data | `$HOME` :sparkle:
`/home/ejabberd/` :orange_circle: | `$HOME`
`/opt/ejabberd/` :sparkle: :low_brightness: | 1086 | | `ejabberdctl` | `ejabberdctl` :sparkle:
`bin/ejabberdctl` :orange_circle: | `bin/ejabberdctl`
`ejabberdctl` :sparkle: :low_brightness: | 1087 | | [`captcha.sh`](#captcha) | `$HOME/bin/captcha.sh` :orange_circle: | `$HOME/bin/captcha.sh` :orange_circle: | 1088 | | `*.sql` files | `$HOME/sql/*.sql` :sparkle: :orange_circle:
`$HOME/database/*.sql` :orange_circle: | `$HOME/database/*.sql`
`$HOME/sql/*.sql` :sparkle: :orange_circle: | 1089 | | Mnesia spool files | `$HOME/database/` :sparkle:
`$HOME/database/NODENAME/` :orange_circle: | `$HOME/database/NODENAME/`
`$HOME/database/` :sparkle: :orange_circle: | 1090 | | :black_square_button: **Variables** | 1091 | | [`EJABBERD_MACRO_*`](#macros-in-environment) | supported :high_brightness: | supported :high_brightness: | 1092 | | Macros used in `ejabberd.yml` | yes :orange_circle: | yes :orange_circle: | 1093 | | [`EJABBERD_MACRO_ADMIN`](#register-admin-account) | Grant admin rights :orange_circle:
(default `admin@localhost`)
| Hardcoded `admin@localhost` | 1094 | | [`REGISTER_ADMIN_PASSWORD`](#register-admin-account) | Register admin account :orange_circle: | unsupported | 1095 | | `CTL_OVER_HTTP` | enabled :orange_circle: | unsupported | 1096 | --------------------------------------------------------------------------------