├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── automatic-issue-label.yml │ ├── bot-create-manual-reminder.yml │ ├── bot-manual-reminder.yml │ ├── bot-remind-stale-pull-requests.yml │ ├── build-and-publish-nightly.yml │ ├── build.yml │ ├── test.yml │ └── update-sh.yml ├── README.md ├── apache ├── Dockerfile ├── docker-entrypoint.sh ├── nonroot-add.txt └── php.ini ├── development ├── Dockerfile └── docker-entrypoint.sh ├── examples ├── README.md ├── docker-compose-fpm-alpine.yaml ├── docker-compose-fpm.yaml ├── docker-compose-mysql.yaml ├── docker-compose-plugins.yaml ├── docker-compose-simple.yaml ├── kubernetes.yaml └── nginx │ └── templates │ └── default.conf.template ├── fpm-alpine ├── Dockerfile ├── docker-entrypoint.sh └── php.ini ├── fpm ├── Dockerfile ├── docker-entrypoint.sh └── php.ini ├── nightly ├── Dockerfile └── README.md ├── templates ├── Dockerfile-alpine.templ ├── Dockerfile-debian.templ ├── docker-entrypoint.sh └── php.ini ├── tests ├── docker-compose.test-apache-postgres.yml ├── docker-compose.test-fpm-postgres.yml ├── nginx-default.conf └── run.sh └── update.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [templates/*.templ] 15 | indent_style = tab 16 | 17 | [Dockerfile] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | templates/*.templ linguist-language=Dockerfile 2 | -------------------------------------------------------------------------------- /.github/workflows/automatic-issue-label.yml: -------------------------------------------------------------------------------- 1 | name: Label new issues 2 | on: 3 | issues: 4 | types: 5 | - reopened 6 | - opened 7 | 8 | jobs: 9 | label_issues: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | steps: 14 | - run: gh issue edit "$NUMBER" --add-label "$LABELS" 15 | env: 16 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | GH_REPO: ${{ github.repository }} 18 | NUMBER: ${{ github.event.issue.number }} 19 | LABELS: "0. to triage" 20 | -------------------------------------------------------------------------------- /.github/workflows/bot-create-manual-reminder.yml: -------------------------------------------------------------------------------- 1 | name: 'Create reminder from comment' 2 | 3 | permissions: 4 | issues: write 5 | pull-requests: write 6 | 7 | on: 8 | issue_comment: 9 | types: [created, edited] 10 | 11 | jobs: 12 | reminder: 13 | if: github.repository == 'roundcube/roundcubemail-docker' 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: 👀 check for reminder 18 | uses: agrc/create-reminder-action@9ff30cde74284045941af16a04362938957253b1 # v1.1.17 19 | -------------------------------------------------------------------------------- /.github/workflows/bot-manual-reminder.yml: -------------------------------------------------------------------------------- 1 | name: 'Notify manually requested reminders' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 * * * *' 6 | 7 | permissions: 8 | issues: write 9 | pull-requests: write 10 | 11 | jobs: 12 | reminder: 13 | if: github.repository == 'roundcube/roundcubemail-docker' 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: check reminders and notify 18 | uses: agrc/reminder-action@96f2ec2e1a7a53ead156504922e9bc36d64f49ee # v1.0.16 19 | -------------------------------------------------------------------------------- /.github/workflows/bot-remind-stale-pull-requests.yml: -------------------------------------------------------------------------------- 1 | name: "Send comment to stale PRs" 2 | on: 3 | schedule: 4 | # Run everyday at midnight 5 | - cron: "0 0 * * *" 6 | 7 | jobs: 8 | review-reminder: 9 | if: github.repository == 'roundcube/roundcubemail-docker' 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: sojusan/github-action-reminder@85a7d4ea6d5535e88e47baa242918a6a654de65d # v1.1.1 13 | with: 14 | github_token: ${{ secrets.GITHUB_TOKEN }} 15 | reminder_message: "🛎️ This PR has had no activity in two weeks." 16 | # Remind after two weeks of inactivity 17 | inactivity_deadline_hours: 336 18 | default_users_to_notify: | 19 | @pabzm 20 | @thomascube 21 | -------------------------------------------------------------------------------- /.github/workflows/build-and-publish-nightly.yml: -------------------------------------------------------------------------------- 1 | name: Build & Publish nightly 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | schedule: 8 | # Rebuild automatically each night 9 | - cron: "4 2 * * *" 10 | 11 | jobs: 12 | build-and-testvariants: 13 | name: Build image and run tests 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - name: Get docker hub username 19 | id: creds 20 | run: echo '::set-output name=username::${{ secrets.DOCKER_PULL_USERNAME }}' 21 | - name: Login to Docker Hub 22 | if: steps.creds.outputs.username != '' 23 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 24 | with: 25 | username: ${{ secrets.DOCKER_PULL_USERNAME }} 26 | password: ${{ secrets.DOCKER_PUSH_PASSWORD }} 27 | 28 | - name: Set up QEMU 29 | uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 30 | - name: Set up Docker Buildx 31 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 32 | with: 33 | buildkitd-flags: --debug 34 | 35 | - name: Build and push nightly image" 36 | uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 37 | with: 38 | context: nightly 39 | platforms: "linux/arm64,linux/arm/v6,linux/arm/v7,linux/386,linux/amd64," 40 | push: true 41 | tags: roundcube/roundcubemail:nightly 42 | # does not work linux/arm/v5 AND linux/mips64le - composer does not support mips64le or armv5 nor does the php image support them on the alpine variant 43 | 44 | - name: Run tests 45 | env: 46 | ROUNDCUBEMAIL_TEST_IMAGE: roundcube/roundcubemail:nightly 47 | run: docker compose -f ./tests/docker-compose.test-apache-postgres.yml up --exit-code-from=sut --abort-on-container-exit 48 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build & Publish 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: 9 | - 'master' 10 | paths-ignore: 11 | - 'README.md' 12 | - 'examples/**' 13 | tags: 14 | - '1.6.*' 15 | schedule: 16 | # Rebuild images each monday early morning to ensure a fresh base OS. 17 | - cron: "23 2 * * 1" 18 | workflow_dispatch: 19 | 20 | jobs: 21 | build-and-testvariants: 22 | name: Build image variants and run tests 23 | runs-on: ubuntu-latest 24 | strategy: 25 | fail-fast: false 26 | max-parallel: 10 27 | matrix: 28 | include: 29 | - variant: 'apache' 30 | test-files: 'apache-postgres' 31 | docker-tag: roundcube/roundcubemail:1.6.x-apache,roundcube/roundcubemail:1.6.11-apache,roundcube/roundcubemail:latest-apache,roundcube/roundcubemail:latest 32 | test-tag: roundcube/roundcubemail:latest-apache 33 | target: 'root' 34 | - variant: 'fpm' 35 | test-files: 'fpm-postgres' 36 | docker-tag: roundcube/roundcubemail:1.6.x-fpm,roundcube/roundcubemail:1.6.11-fpm,roundcube/roundcubemail:latest-fpm 37 | test-tag: roundcube/roundcubemail:latest-fpm 38 | target: 'root' 39 | - variant: 'fpm-alpine' 40 | test-files: 'fpm-postgres' 41 | docker-tag: roundcube/roundcubemail:1.6.x-fpm-alpine,roundcube/roundcubemail:1.6.11-fpm-alpine,roundcube/roundcubemail:latest-fpm-alpine 42 | test-tag: roundcube/roundcubemail:latest-fpm-alpine 43 | target: 'root' 44 | - variant: 'apache' 45 | test-files: 'apache-postgres' 46 | docker-tag: roundcube/roundcubemail:1.6.x-apache-nonroot,roundcube/roundcubemail:1.6.11-apache-nonroot,roundcube/roundcubemail:latest-apache-nonroot,roundcube/roundcubemail:latest-nonroot 47 | test-tag: roundcube/roundcubemail:latest-apache-nonroot 48 | target: 'nonroot' 49 | http-port: '8000' 50 | - variant: 'fpm' 51 | test-files: 'fpm-postgres' 52 | docker-tag: roundcube/roundcubemail:1.6.x-fpm-nonroot,roundcube/roundcubemail:1.6.11-fpm-nonroot,roundcube/roundcubemail:latest-fpm-nonroot 53 | test-tag: roundcube/roundcubemail:latest-fpm-nonroot 54 | target: 'nonroot' 55 | - variant: 'fpm-alpine' 56 | test-files: 'fpm-postgres' 57 | docker-tag: roundcube/roundcubemail:1.6.x-fpm-alpine-nonroot,roundcube/roundcubemail:1.6.11-fpm-alpine-nonroot,roundcube/roundcubemail:latest-fpm-alpine-nonroot 58 | test-tag: roundcube/roundcubemail:latest-fpm-alpine-nonroot 59 | target: 'nonroot' 60 | - variant: 'development' 61 | test-files: 'development' 62 | docker-tag: roundcube/roundcubemail:development 63 | test-tag: roundcube/roundcubemail:development 64 | steps: 65 | - name: Checkout repository 66 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 67 | - name: Get docker hub username 68 | id: creds 69 | run: echo '::set-output name=username::${{ secrets.DOCKER_PULL_USERNAME }}' 70 | - name: Login to Docker Hub 71 | if: steps.creds.outputs.username != '' 72 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 73 | with: 74 | username: ${{ secrets.DOCKER_PULL_USERNAME }} 75 | password: ${{ secrets.DOCKER_PUSH_PASSWORD }} 76 | 77 | - name: Set up QEMU 78 | uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 79 | - name: Set up Docker Buildx 80 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 81 | with: 82 | buildkitd-flags: --debug 83 | 84 | - name: Build and push image for "${{ matrix.variant }}" 85 | uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 86 | with: 87 | context: ${{ matrix.variant }} 88 | platforms: "linux/arm64,linux/arm/v6,linux/arm/v7,linux/386,linux/amd64," 89 | push: true 90 | tags: ${{ matrix.docker-tag }} 91 | target: ${{ matrix.target }} 92 | # does not work linux/arm/v5 AND linux/mips64le - composer does not support mips64le or armv5 nor does the php image support them on the alpine variant 93 | 94 | - name: Run tests 95 | env: 96 | ROUNDCUBEMAIL_TEST_IMAGE: ${{ matrix.test-tag }} 97 | HTTP_PORT: ${{ matrix.http-port || '80' }} 98 | run: | 99 | set -exu; 100 | for testFile in ${{ join(matrix.test-files, ' ') }}; 101 | do 102 | docker compose -f ./tests/docker-compose.test-${testFile}.yml \ 103 | up --exit-code-from=sut --abort-on-container-exit 104 | done 105 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: {} 8 | push: 9 | branches: 10 | - '!master' 11 | jobs: 12 | build-and-testvariants: 13 | name: Build image variants and run tests 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | max-parallel: 10 18 | matrix: 19 | include: 20 | - variant: 'apache' 21 | test-files: 'apache-postgres' 22 | docker-tag: roundcube/roundcubemail:test-apache 23 | target: 'root' 24 | - variant: 'fpm' 25 | test-files: 'fpm-postgres' 26 | docker-tag: roundcube/roundcubemail:test-fpm 27 | target: 'root' 28 | - variant: 'fpm-alpine' 29 | test-files: 'fpm-postgres' 30 | docker-tag: roundcube/roundcubemail:test-fpm-alpine 31 | target: 'root' 32 | - variant: 'apache' 33 | test-files: 'apache-postgres' 34 | docker-tag: roundcube/roundcubemail:test-apache-nonroot 35 | target: 'nonroot' 36 | http-port: '8000' 37 | - variant: 'fpm' 38 | test-files: 'fpm-postgres' 39 | docker-tag: roundcube/roundcubemail:test-fpm-nonroot 40 | target: 'nonroot' 41 | - variant: 'fpm-alpine' 42 | test-files: 'fpm-postgres' 43 | docker-tag: roundcube/roundcubemail:test-fpm-alpine-nonroot 44 | target: 'nonroot' 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 48 | - name: Get docker hub username 49 | id: creds 50 | run: echo '::set-output name=username::${{ secrets.DOCKER_PULL_USERNAME }}' 51 | - name: Login to Docker Hub 52 | if: steps.creds.outputs.username != '' 53 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 54 | with: 55 | username: ${{ secrets.DOCKER_PULL_USERNAME }} 56 | password: ${{ secrets.DOCKER_PULL_PASSWORD }} 57 | 58 | - name: Build image for "${{ matrix.variant }} / ${{ matrix.target }}" 59 | run: cd ${{ matrix.variant }} && docker buildx build ./ -t ${{ matrix.docker-tag }} --target ${{ matrix.target }} 60 | - name: Run tests 61 | env: 62 | ROUNDCUBEMAIL_TEST_IMAGE: ${{ matrix.docker-tag }} 63 | HTTP_PORT: ${{ matrix.http-port || '80' }} 64 | run: | 65 | set -exu; 66 | for testFile in ${{ join(matrix.test-files, ' ') }}; 67 | do 68 | docker compose -f ./tests/docker-compose.test-${testFile}.yml \ 69 | up --exit-code-from=sut --abort-on-container-exit 70 | done 71 | -------------------------------------------------------------------------------- /.github/workflows/update-sh.yml: -------------------------------------------------------------------------------- 1 | name: update.sh 2 | 3 | permissions: 4 | # Git push permissions are needed 5 | contents: write 6 | pull-requests: write 7 | 8 | on: 9 | push: 10 | branches: 11 | - master 12 | schedule: 13 | - cron: '11 0 * * *' 14 | workflow_dispatch: 15 | 16 | jobs: 17 | run_update_sh: 18 | name: Run update.sh script 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | token: ${{ secrets.WOKFLOW_TOKEN }} 24 | - name: Run update.sh script 25 | run: ./update.sh 26 | - name: Commit files 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | run: | 30 | # Exit early if no changes are present. 31 | test $(git status --porcelain | wc -l) -gt 0 || { echo "No changes to commit, happily cancelling this script."; exit 0; } 32 | # Use a distinct branch-name (nano-seconds should be good enough). 33 | BRANCH="changes-from-update.sh-$(date +'%Y-%m-%d_%H%M%S_%N')" 34 | git switch -C "$BRANCH" 35 | git config --local user.email "workflow@github.com" 36 | git config --local user.name "GitHub Workflow" 37 | git add -A 38 | git commit -m "Update roundcube version (via update.sh)" 39 | git push --set-upstream origin "$BRANCH" 40 | gh pr create -B master -H "$BRANCH" --title 'Changes from update.sh' --body "These are the changes of the automated run of ./update.sh" --assignee pabzm 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Build & Test](https://github.com/roundcube/roundcubemail-docker/actions/workflows/test.yml/badge.svg)](https://github.com/roundcube/roundcubemail-docker/actions/workflows/test.yml) 3 | [![Docker Pulls](https://img.shields.io/docker/pulls/roundcube/roundcubemail.svg)](https://hub.docker.com/r/roundcube/roundcubemail/) 4 | 5 | # Running Roundcube in a Docker Container 6 | 7 | The simplest method is to run the official image: 8 | 9 | ```sh 10 | docker run -e ROUNDCUBEMAIL_DEFAULT_HOST=mail -e ROUNDCUBEMAIL_SMTP_SERVER=mail -p 8000:80 -d roundcube/roundcubemail 11 | ``` 12 | 13 | where `mail` should be replaced by your host name for the IMAP and SMTP server. 14 | 15 | ## Tags and Variants 16 | 17 | Roundcube comes in three different variants (`apache`, `fpm` and `fpm-alpine`) which are all built on top of official `php` images of the same variants. 18 | 19 | The `latest-*` tags always contain the **latest stable** version of Roundcube Webmail with the latest version of the `php` base images available. For recent major versions of Roundcube we have tags like `1.3.x`. Those are continuously updated with versions of the according release series and updates to the base images. 20 | 21 | We also publish full version tags (e.g. `1.3.10`) but these just represent the version and base image at the time of the release. These tags do not receive any updates. 22 | 23 | ## Configuration/Environment Variables 24 | 25 | The following env variables can be set to configure your Roundcube Docker instance: 26 | 27 | `ROUNDCUBEMAIL_DEFAULT_HOST` - Hostname of the IMAP server to connect to. For encrypted connections, prefix the host with `tls://` (STARTTLS) or `ssl://` (SSL/TLS). 28 | 29 | `ROUNDCUBEMAIL_DEFAULT_PORT` - IMAP port number; defaults to `143` 30 | 31 | `ROUNDCUBEMAIL_SMTP_SERVER` - Hostname of the SMTP server to send mails. For encrypted connections, prefix the host with `tls://` (STARTTLS) or `ssl://` (SSL/TLS). 32 | 33 | `ROUNDCUBEMAIL_SMTP_PORT` - SMTP port number; defaults to `587` 34 | 35 | `ROUNDCUBEMAIL_USERNAME_DOMAIN` - Automatically add this domain to user names for login. See [defaults.inc.php](https://github.com/roundcube/roundcubemail/blob/master/config/defaults.inc.php) for more information. 36 | 37 | `ROUNDCUBEMAIL_REQUEST_PATH` - Specify request path with reverse proxy; defaults to `/`. See [defaults.inc.php](https://github.com/roundcube/roundcubemail/blob/master/config/defaults.inc.php) for possible values. 38 | 39 | `ROUNDCUBEMAIL_PLUGINS` - List of built-in plugins to activate. Defaults to `archive,zipdownload` 40 | 41 | `ROUNDCUBEMAIL_COMPOSER_PLUGINS` - The list of composer packages to install on startup. Use `ROUNDCUBEMAIL_PLUGINS` to enable them. 42 | 43 | `ROUNDCUBEMAIL_SKIN` - Configures the default theme. Defaults to `elastic` 44 | 45 | `ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE` - File upload size limit; defaults to `5M` 46 | 47 | `ROUNDCUBEMAIL_SPELLCHECK_URI` - Fully qualified URL to a Google XML spell check API like [google-spell-pspell](https://github.com/roundcube/google-spell-pspell) 48 | 49 | `ROUNDCUBEMAIL_ASPELL_DICTS` - List of aspell dictionaries to install for spell checking (comma-separated, e.g. `de,fr,pl`). 50 | 51 | By default, the image will use a local SQLite database for storing user account metadata. 52 | It'll be created inside the container directory `/var/roundcube/db`. In order to persist the database, a volume 53 | mount should be added to this path. 54 | (For production environments, please assess individually if SQLite is the right database choice.) 55 | 56 | ### Connect to a Database 57 | 58 | The recommended way to run Roundcube is connected to a MySQL database. Specify the following env variables to do so: 59 | 60 | `ROUNDCUBEMAIL_DB_TYPE` - Database provider; currently supported: `mysql`, `pgsql`, `sqlite` 61 | 62 | `ROUNDCUBEMAIL_DB_HOST` - Host (or Docker instance) name of the database service; defaults to `mysql` or `postgres` depending on linked containers. 63 | 64 | `ROUNDCUBEMAIL_DB_PORT` - Port number of the database service; defaults to `3306` or `5432` depending on linked containers. 65 | 66 | `ROUNDCUBEMAIL_DB_USER` - The database username for Roundcube; defaults to `root` on `mysql` 67 | 68 | `ROUNDCUBEMAIL_DB_PASSWORD` - The password for the database connection 69 | 70 | `ROUNDCUBEMAIL_DB_NAME` - The database name for Roundcube to use; defaults to `roundcubemail` 71 | 72 | Before starting the container, please make sure that the supplied database exists and the given database user 73 | has privileges to create tables. 74 | 75 | Run it with a link to the MySQL host and the username/password variables: 76 | 77 | ```sh 78 | docker run --link=mysql:mysql -d roundcube/roundcubemail 79 | ``` 80 | 81 | ## Persistent data 82 | 83 | The Roundcube containers do not store any data persistently by default. There are, however, 84 | some directories that could be mounted as volume or bind mount to share data between containers 85 | or to inject additional data into the container: 86 | 87 | * `/var/www/html`: Roundcube installation directory 88 | This is the document root of Roundcube. Plugins and additional skins are stored here amongst the Roundcube sources. 89 | Share this directory when using the FPM variant and let a webserver container serve the static files from here. 90 | 91 | * `/var/roundcube/config`: Location for additional config files 92 | See the [Advanced configuration](#advanced-configuration) section for details. 93 | 94 | * `/var/roundcube/db`: storage location of the SQLite database 95 | Only needed if using `ROUNDCUBEMAIL_DB_TYPE=sqlite` to persist the Roundcube database. 96 | 97 | * `/var/roundcube/enigma`: storage location of the enigma plugin 98 | If enabled, the "enigma" plugin stores OpenPGP keys here. 99 | 100 | * `/tmp/roundcube-temp`: Roundcube's temp folder 101 | Temp files like uploaded attachments or thumbnail images are stored here. 102 | Share this directory via a volume when running multiple replicas of the roundcube container. 103 | 104 | ## Docker Secrets 105 | 106 | When running the Roundcube container in a Docker Swarm, you can use [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/) 107 | to share credentials across all instances. The following secrets are currently supported by Roundcube: 108 | 109 | * `roundcube_des_key`: Unique and random key for encryption purposes 110 | * `roundcube_db_user`: Database connection username (mappend to `ROUNDCUBEMAIL_DB_USER`) 111 | * `roundcube_db_password`: Database connection password (mappend to `ROUNDCUBEMAIL_DB_PASSWORD`) 112 | * `roundcube_oauth_client_secret`: OAuth client secret (mappend to `ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET`) 113 | 114 | ## Advanced configuration 115 | 116 | Apart from the above described environment variables, the Docker image also allows to add custom config files 117 | which are merged into Roundcube's default config. Therefore the image defines the path `/var/roundcube/config` 118 | where additional config files (`*.php`) are searched and included. Mount a local directory with your config 119 | files - check for valid PHP syntax - when starting the Docker container: 120 | 121 | ```sh 122 | docker run -v ./config/:/var/roundcube/config/ -d roundcube/roundcubemail 123 | ``` 124 | 125 | Check the Roundcube Webmail wiki for a reference of [Roundcube config options](https://github.com/roundcube/roundcubemail/wiki/Configuration). 126 | 127 | Customized PHP settings can be implemented by mounting a configuration file to `/usr/local/etc/php/conf.d/zzz_roundcube-custom.ini`. 128 | For example, it may be used to increase the PHP memory limit (`memory_limit=128M`). 129 | 130 | ## Installing Roundcube Plugins 131 | 132 | With the latest updates, the Roundcube image is now able to install plugins. 133 | You need to fill `ROUNDCUBEMAIL_COMPOSER_PLUGINS` with the list of composer packages to install. 134 | And set them in `ROUNDCUBEMAIL_PLUGINS` in order to enable the installed plugins. 135 | 136 | For example: 137 | 138 | ```yaml 139 | ROUNDCUBEMAIL_COMPOSER_PLUGINS: "weird-birds/thunderbird_labels,jfcherng-roundcube/show-folder-size,germancoding/tls_icon:^1.2" 140 | ROUNDCUBEMAIL_PLUGINS: thunderbird_labels, show_folder_size, tls_icon 141 | ``` 142 | 143 | ## HTTPS 144 | 145 | Currently all images are configured to speak HTTP. To provide HTTPS please run an additional reverse proxy in front of them, which handles certificates and terminates TLS. Alternatively you could derive from our images (or use the advanced configuration methods) to make Apache or nginx provide HTTPS – but please refrain from opening issues asking for support with such a setup. 146 | 147 | 148 | ## Examples 149 | 150 | A few example setups using `docker-compose` can be found in our [Github repository](https://github.com/roundcube/roundcubemail-docker/tree/master/examples). 151 | 152 | ## Building a Docker image 153 | 154 | Use the `Dockerfile` in this repository to build your own Docker image. 155 | It pulls the latest build of Roundcube Webmail from the Github download page and builds it on top of a `php:7.4-apache` Docker image. 156 | 157 | Build it from one of the variants directories with 158 | 159 | ```sh 160 | docker build -t roundcubemail . 161 | ``` 162 | 163 | You can also create your own Docker image by extending from this image. 164 | 165 | For instance, you could extend this image to add composer and install requirements for special plugins: 166 | 167 | ```Dockerfile 168 | FROM roundcube/roundcubemail:latest 169 | 170 | # COMPOSER_ALLOW_SUPERUSER is needed to run plugins when using a container 171 | ENV COMPOSER_ALLOW_SUPERUSER=1 172 | 173 | RUN set -ex; \ 174 | apt-get update; \ 175 | apt-get install -y --no-install-recommends \ 176 | git \ 177 | ; \ 178 | ``` 179 | 180 | # License 181 | 182 | This program is free software: you can redistribute it and/or modify it under the terms 183 | of the GNU General Public License as published by the Free Software Foundation, either 184 | version 3 of the License, or (at your option) any later version. 185 | 186 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 187 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 188 | See the GNU General Public License for more details. 189 | 190 | For more details about licensing and the exceptions for skins and plugins 191 | see [roundcube.net/license][license]. 192 | -------------------------------------------------------------------------------- /apache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-apache as root 2 | LABEL maintainer="Thomas Bruederli " 3 | LABEL org.opencontainers.image.source="https://github.com/roundcube/roundcubemail-docker" 4 | 5 | # This should be done by the upstream images, but as long as they don't do it, 6 | # we rather use our own hands than suffer from outdated packages. 7 | # Kept as standalone command to make it stand out and be easy to remove. 8 | RUN apt-get update && apt-get -y upgrade && apt-get clean 9 | 10 | RUN set -ex; \ 11 | if [ "apache" = "apache" ]; then \ 12 | a2enmod rewrite; \ 13 | # Make Apache use public_html/ as document root to protect files outside of it \ 14 | # against unauthorized access. \ 15 | # This is possible and recommended since a while, and will be required for Roundcubemail v1.7. \ 16 | sed -i -e 's|\(DocumentRoot /var/www/html\)$|\1/public_html|' /etc/apache2/sites-available/000-default.conf; \ 17 | fi; \ 18 | apt-get update; \ 19 | \ 20 | savedAptMark="$(apt-mark showmanual)"; \ 21 | \ 22 | apt-get install -y --no-install-recommends \ 23 | libfreetype6-dev \ 24 | libicu-dev \ 25 | libjpeg62-turbo-dev \ 26 | libldap2-dev \ 27 | libmagickwand-dev \ 28 | libpng-dev \ 29 | libpq-dev \ 30 | libsqlite3-dev \ 31 | libzip-dev \ 32 | libpspell-dev \ 33 | libonig-dev \ 34 | libldap-common \ 35 | ; \ 36 | # installto.sh & web install dependencies 37 | fetchDeps="gnupg locales libc-l10n"; \ 38 | installDeps="aspell aspell-en rsync unzip"; \ 39 | apt-get install -y --no-install-recommends \ 40 | $installDeps \ 41 | $fetchDeps \ 42 | ; \ 43 | \ 44 | # Extract sources to avoid using pecl (https://github.com/docker-library/php/issues/374#issuecomment-690698974) 45 | pecl bundle -d /usr/src/php/ext imagick; \ 46 | pecl bundle -d /usr/src/php/ext redis; \ 47 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 48 | docker-php-ext-configure gd --with-jpeg --with-freetype; \ 49 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 50 | docker-php-ext-install \ 51 | exif \ 52 | gd \ 53 | intl \ 54 | ldap \ 55 | pdo_mysql \ 56 | pdo_pgsql \ 57 | pdo_sqlite \ 58 | zip \ 59 | pspell \ 60 | imagick \ 61 | redis \ 62 | ; \ 63 | docker-php-ext-enable imagick opcache redis; \ 64 | docker-php-source delete; \ 65 | rm -r /tmp/pear; \ 66 | # Display installed modules 67 | php -m; \ 68 | \ 69 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 70 | apt-mark auto '.*' > /dev/null; \ 71 | apt-mark manual $savedAptMark $installDeps $fetchDeps; \ 72 | extdir="$(php -r 'echo ini_get("extension_dir");')"; \ 73 | ldd "$extdir"/*.so \ 74 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 75 | | sort -u \ 76 | | xargs -r dpkg-query -S \ 77 | | cut -d: -f1 \ 78 | | sort -u \ 79 | | xargs -rt apt-mark manual; \ 80 | \ 81 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 82 | rm -rf /var/lib/apt/lists/*; \ 83 | ldd "$extdir"/*.so | grep -qzv "=> not found" || (echo "Sanity check failed: missing libraries:"; ldd "$extdir"/*.so | grep " => not found"; exit 1); \ 84 | ldd "$extdir"/*.so | grep -q "libzip.so.* => .*/libzip.so.*" || (echo "Sanity check failed: libzip.so is not referenced"; ldd "$extdir"/*.so; exit 1); \ 85 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 86 | [ -z "$err" ] || (echo "Sanity check failed: php returned errors; $err"; exit 1;); \ 87 | # include the wait-for-it.sh script (latest commit) 88 | curl -fL https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh -o /wait-for-it.sh; \ 89 | chmod +x /wait-for-it.sh; 90 | 91 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 92 | 93 | # Use the default production configuration 94 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 95 | 96 | # use custom PHP settings 97 | COPY php.ini /usr/local/etc/php/conf.d/roundcube-defaults.ini 98 | 99 | COPY --chmod=0755 docker-entrypoint.sh / 100 | 101 | # Define Roundcubemail version 102 | ENV ROUNDCUBEMAIL_VERSION 1.6.11 103 | 104 | # Define the GPG key used for the bundle verification process 105 | ENV ROUNDCUBEMAIL_KEYID "F3E4 C04B B3DB 5D42 15C4 5F7F 5AB2 BAA1 41C4 F7D5" 106 | 107 | # Download package and extract to web volume 108 | RUN set -ex; \ 109 | curl -o roundcubemail.tar.gz -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz; \ 110 | curl -o roundcubemail.tar.gz.asc -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz.asc; \ 111 | export GNUPGHOME="$(mktemp -d)"; \ 112 | curl -fSL https://roundcube.net/download/pubkey.asc -o /tmp/pubkey.asc; \ 113 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o 'Key fingerprint') != 1 ]; then echo 'The key file should contain only one GPG key'; exit 1; fi; \ 114 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o "${ROUNDCUBEMAIL_KEYID}") != 1 ]; then echo 'The key ID should be the roundcube one'; exit 1; fi; \ 115 | gpg --batch --import /tmp/pubkey.asc; \ 116 | rm /tmp/pubkey.asc; \ 117 | gpg --batch --verify roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 118 | gpgconf --kill all; \ 119 | mkdir /usr/src/roundcubemail; \ 120 | tar -xf roundcubemail.tar.gz -C /usr/src/roundcubemail --strip-components=1 --no-same-owner; \ 121 | rm -r "$GNUPGHOME" roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 122 | rm -rf /usr/src/roundcubemail/installer; \ 123 | chown -R www-data:www-data /usr/src/roundcubemail/logs; \ 124 | # Create the config dir 125 | mkdir -p /var/roundcube/config /var/roundcube/enigma; \ 126 | chown -R www-data:www-data /var/roundcube; \ 127 | chmod +t /var/roundcube 128 | 129 | ENTRYPOINT ["/docker-entrypoint.sh"] 130 | CMD ["apache2-foreground"] 131 | 132 | 133 | #### non-root stage 134 | 135 | FROM root as nonroot 136 | 137 | # Prepare locale config for locale-gen 138 | RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen; \ 139 | /usr/sbin/locale-gen 140 | 141 | RUN sed -i 's/^Listen 80$/Listen 8000/' /etc/apache2/ports.conf 142 | RUN sed -i /etc/apache2/sites-enabled/000-default.conf -e 's/:80>/:8000>/' 143 | 144 | EXPOSE 8000 145 | 146 | USER 33:33 -------------------------------------------------------------------------------- /apache/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -ex 3 | 4 | # PWD=`pwd` 5 | 6 | if [[ "$1" == apache2* || "$1" == php-fpm || "$1" == bin* ]]; then 7 | INSTALLDIR=`pwd` 8 | # docroot is empty 9 | if ! [ -e index.php -a -e bin/installto.sh ]; then 10 | echo >&2 "roundcubemail not found in $PWD - copying now..." 11 | if [ "$(ls -A)" ]; then 12 | echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" 13 | ( set -x; ls -A; sleep 10 ) 14 | fi 15 | tar cf - --one-file-system -C /usr/src/roundcubemail . | tar xf - 16 | echo >&2 "Complete! ROUNDCUBEMAIL has been successfully copied to $INSTALLDIR" 17 | # update Roundcube in docroot 18 | else 19 | echo >&2 "roundcubemail found in $INSTALLDIR - installing update..." 20 | (cd /usr/src/roundcubemail && bin/installto.sh -y $INSTALLDIR) 21 | # Re-install composer modules (including plugins) 22 | composer \ 23 | --working-dir=${INSTALLDIR} \ 24 | --prefer-dist \ 25 | --no-dev \ 26 | --no-interaction \ 27 | --optimize-autoloader \ 28 | install 29 | fi 30 | 31 | if [ -f /run/secrets/roundcube_db_user ]; then 32 | ROUNDCUBEMAIL_DB_USER=`cat /run/secrets/roundcube_db_user` 33 | fi 34 | if [ -f /run/secrets/roundcube_db_password ]; then 35 | ROUNDCUBEMAIL_DB_PASSWORD=`cat /run/secrets/roundcube_db_password` 36 | fi 37 | if [ -f /run/secrets/roundcube_oauth_client_secret ]; then 38 | ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET=`cat /run/secrets/roundcube_oauth_client_secret` 39 | fi 40 | 41 | if [ ! -z "${!POSTGRES_ENV_POSTGRES_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "pgsql" ]; then 42 | : "${ROUNDCUBEMAIL_DB_TYPE:=pgsql}" 43 | : "${ROUNDCUBEMAIL_DB_HOST:=postgres}" 44 | : "${ROUNDCUBEMAIL_DB_PORT:=5432}" 45 | : "${ROUNDCUBEMAIL_DB_USER:=${POSTGRES_ENV_POSTGRES_USER}}" 46 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${POSTGRES_ENV_POSTGRES_PASSWORD}}" 47 | : "${ROUNDCUBEMAIL_DB_NAME:=${POSTGRES_ENV_POSTGRES_DB:-roundcubemail}}" 48 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 49 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 50 | # DO NOT USE wait-for-it.sh 51 | else 52 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 53 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 54 | fi 55 | elif [ ! -z "${!MYSQL_ENV_MYSQL_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "mysql" ]; then 56 | : "${ROUNDCUBEMAIL_DB_TYPE:=mysql}" 57 | : "${ROUNDCUBEMAIL_DB_HOST:=mysql}" 58 | : "${ROUNDCUBEMAIL_DB_PORT:=3306}" 59 | : "${ROUNDCUBEMAIL_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" 60 | if [ "$ROUNDCUBEMAIL_DB_USER" = 'root' ]; then 61 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD}}" 62 | else 63 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD}}" 64 | fi 65 | : "${ROUNDCUBEMAIL_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-roundcubemail}}" 66 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 67 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 68 | # DO NOT USE wait-for-it.sh 69 | else 70 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 71 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 72 | fi 73 | else 74 | # use local SQLite DB in /var/roundcube/db 75 | : "${ROUNDCUBEMAIL_DB_TYPE:=sqlite}" 76 | : "${ROUNDCUBEMAIL_DB_DIR:=/var/roundcube/db}" 77 | : "${ROUNDCUBEMAIL_DB_NAME:=sqlite}" 78 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}:///$ROUNDCUBEMAIL_DB_DIR/${ROUNDCUBEMAIL_DB_NAME}.db?mode=0646}" 79 | 80 | mkdir -p $ROUNDCUBEMAIL_DB_DIR 81 | chown www-data:www-data $ROUNDCUBEMAIL_DB_DIR 82 | fi 83 | 84 | : "${ROUNDCUBEMAIL_DEFAULT_HOST:=localhost}" 85 | : "${ROUNDCUBEMAIL_DEFAULT_PORT:=143}" 86 | : "${ROUNDCUBEMAIL_SMTP_SERVER:=localhost}" 87 | : "${ROUNDCUBEMAIL_SMTP_PORT:=587}" 88 | : "${ROUNDCUBEMAIL_PLUGINS:=archive,zipdownload}" 89 | : "${ROUNDCUBEMAIL_SKIN:=elastic}" 90 | : "${ROUNDCUBEMAIL_TEMP_DIR:=/tmp/roundcube-temp}" 91 | : "${ROUNDCUBEMAIL_REQUEST_PATH:=/}" 92 | : "${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER:=$INSTALLDIR}" 93 | 94 | if [ ! -z "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" ]; then 95 | echo "Installing plugins from the list" 96 | echo "Plugins: ${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" 97 | 98 | # Change ',' into a space 99 | ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH=`echo "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" | tr ',' ' '` 100 | 101 | composer \ 102 | --working-dir=${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER} \ 103 | --prefer-dist \ 104 | --prefer-stable \ 105 | --update-no-dev \ 106 | --no-interaction \ 107 | --optimize-autoloader \ 108 | require \ 109 | -- \ 110 | ${ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH}; 111 | fi 112 | 113 | if [ ! -d skins/${ROUNDCUBEMAIL_SKIN} ]; then 114 | # Installing missing skin 115 | echo "Installing missing skin: ${ROUNDCUBEMAIL_SKIN}" 116 | composer \ 117 | --working-dir=${INSTALLDIR} \ 118 | --prefer-dist \ 119 | --prefer-stable \ 120 | --update-no-dev \ 121 | --no-interaction \ 122 | --optimize-autoloader \ 123 | require \ 124 | -- \ 125 | roundcube/${ROUNDCUBEMAIL_SKIN}; 126 | fi 127 | 128 | if [ ! -e config/config.inc.php ]; then 129 | GENERATED_DES_KEY=`head /dev/urandom | base64 | head -c 24` 130 | touch config/config.inc.php 131 | 132 | echo "Write root config to $PWD/config/config.inc.php" 133 | echo " config/config.inc.php 142 | 143 | elif ! grep -q "config.docker.inc.php" config/config.inc.php; then 144 | echo "include(__DIR__ . '/config.docker.inc.php');" >> config/config.inc.php 145 | fi 146 | 147 | ROUNDCUBEMAIL_PLUGINS_PHP=`echo "${ROUNDCUBEMAIL_PLUGINS}" | sed -E "s/[, ]+/', '/g"` 148 | echo "Write Docker config to $PWD/config/config.docker.inc.php" 149 | echo " config/config.docker.inc.php 160 | 161 | if [ -e /run/secrets/roundcube_des_key ]; then 162 | echo "\$config['des_key'] = file_get_contents('/run/secrets/roundcube_des_key');" >> config/config.docker.inc.php 163 | elif [ ! -z "${ROUNDCUBEMAIL_DES_KEY}" ]; then 164 | echo "\$config['des_key'] = getenv('ROUNDCUBEMAIL_DES_KEY');" >> config/config.docker.inc.php 165 | fi 166 | 167 | if [ ! -z "${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}" ]; then 168 | echo "\$config['oauth_client_secret'] = '${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}';" >> config/config.docker.inc.php 169 | fi 170 | 171 | if [ ! -z "${ROUNDCUBEMAIL_SPELLCHECK_URI}" ]; then 172 | echo "\$config['spellcheck_engine'] = 'googie';" >> config/config.docker.inc.php 173 | echo "\$config['spellcheck_uri'] = '${ROUNDCUBEMAIL_SPELLCHECK_URI}';" >> config/config.docker.inc.php 174 | fi 175 | 176 | # If the "enigma" plugin is enabled but has no storage configured, inject a default value for the mandatory setting. 177 | if $(echo $ROUNDCUBEMAIL_PLUGINS | grep -Eq '\benigma\b') && ! grep -qr enigma_pgp_homedir /var/roundcube/config/; then 178 | echo "\$config['enigma_pgp_homedir'] = '/var/roundcube/enigma';" >> config/config.docker.inc.php 179 | fi 180 | 181 | # include custom config files 182 | for fn in `ls /var/roundcube/config/*.php 2>/dev/null || true`; do 183 | echo "include('$fn');" >> config/config.docker.inc.php 184 | done 185 | 186 | # initialize or update DB 187 | bin/initdb.sh --dir=$PWD/SQL --update || echo "Failed to initialize/update the database. Please start with an empty database and restart the container." 188 | 189 | if [ ! -z "${ROUNDCUBEMAIL_TEMP_DIR}" ]; then 190 | mkdir -p ${ROUNDCUBEMAIL_TEMP_DIR} && chown www-data ${ROUNDCUBEMAIL_TEMP_DIR} 191 | fi 192 | 193 | if [ ! -z "${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" ]; then 194 | echo "upload_max_filesize=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 195 | echo "post_max_size=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 196 | fi 197 | 198 | : "${ROUNDCUBEMAIL_LOCALE:=en_US.UTF-8 UTF-8}" 199 | 200 | if [ -e /usr/sbin/locale-gen ] && [ ! -f /etc/locale.gen ] && [ ! -z "${ROUNDCUBEMAIL_LOCALE}" ]; then 201 | echo "${ROUNDCUBEMAIL_LOCALE}" > /etc/locale.gen && /usr/sbin/locale-gen 202 | fi 203 | 204 | if [ ! -z "${ROUNDCUBEMAIL_ASPELL_DICTS}" ]; then 205 | ASPELL_PACKAGES=`echo -n "aspell-${ROUNDCUBEMAIL_ASPELL_DICTS}" | sed -E "s/[, ]+/ aspell-/g"` 206 | which apt-get && apt-get update && apt-get install -y $ASPELL_PACKAGES 207 | which apk && apk add --no-cache $ASPELL_PACKAGES 208 | fi 209 | 210 | fi 211 | 212 | exec "$@" 213 | -------------------------------------------------------------------------------- /apache/nonroot-add.txt: -------------------------------------------------------------------------------- 1 | RUN sed -i 's/^Listen 80$/Listen 8000/' /etc/apache2/ports.conf 2 | RUN sed -i /etc/apache2/sites-enabled/000-default.conf -e 's/:80>/:8000>/' 3 | 4 | EXPOSE 8000 5 | -------------------------------------------------------------------------------- /apache/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=64M 2 | display_errors=Off 3 | log_errors=On 4 | upload_max_filesize=5M 5 | post_max_size=6M 6 | zlib.output_compression=Off 7 | session.auto_start=Off 8 | session.gc_maxlifetime=21600 9 | session.gc_divisor=500 10 | session.gc_probability=1 11 | -------------------------------------------------------------------------------- /development/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM docker.io/roundcube/roundcubemail:latest-apache-nonroot 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | USER root 6 | 7 | RUN apt-get update \ 8 | && apt-get install -y --no-install-recommends npm git sudo \ 9 | && apt-get clean 10 | 11 | COPY --chmod=0755 docker-entrypoint.sh / 12 | 13 | # Prevent the upstream image from overwriting our code 14 | RUN rm -r /usr/src/roundcubemail 15 | 16 | RUN install -d -o www-data -g www-data /var/roundcube 17 | 18 | # Pre-download js dependencies (these don't change much over time). 19 | ENV CACHEDIR=/var/cache/roundcubemail/jsdeps 20 | RUN install -o www-data -d "$CACHEDIR" 21 | # Create NPM's home directory so it can write cache files and logs. 22 | RUN install -o www-data -d /var/www/.npm 23 | 24 | USER www-data 25 | 26 | # We need the code from the repo, not from a released tarball. 27 | RUN cd /tmp \ 28 | && curl -OLs https://github.com/roundcube/roundcubemail/archive/refs/heads/master.zip \ 29 | && unzip -q master.zip \ 30 | && ./roundcubemail-master/bin/install-jsdeps.sh \ 31 | && rm -rf master.zip roundcubemail-master 32 | 33 | VOLUME /var/www/html 34 | -------------------------------------------------------------------------------- /development/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | if [[ ! -f index.php ]]; then 4 | echo "Error: No source code in /var/www/html – you must mount your code base to that path!" 5 | exit 1 6 | fi 7 | 8 | if [[ "$1" != apache2* && "$1" != php-fpm && "$1" != bin* ]]; then 9 | exec $@ 10 | fi 11 | 12 | # Ensure two very essential requirements are set in the config file. 13 | if ! grep -q "log_driver.*stdout" config/config.inc.php; then 14 | echo "\$config['log_driver'] = 'stdout';" >> config/config.inc.php 15 | fi 16 | if ! grep -q "db_dsnw" config/config.inc.php; then 17 | echo "\$config['db_dsnw'] = 'sqlite:////var/roundcube/sqlite.db?mode=0644';" >> config/config.inc.php 18 | fi 19 | 20 | # Run the steps necessary to actually use the repository code. 21 | 22 | # Install dependencies 23 | if [[ ! -f composer.json ]]; then 24 | # For older versions of Roundcubemail. 25 | cp -v composer.json-dist composer.json 26 | fi 27 | composer --prefer-dist --no-interaction --optimize-autoloader install 28 | 29 | # Download external Javascript dependencies. 30 | bin/install-jsdeps.sh 31 | 32 | # Translate elastic's styles to CSS. 33 | if grep -q css-elastic Makefile; then 34 | make css-elastic 35 | else 36 | # Older versions 37 | ( 38 | npm install less && \ 39 | npm install less-plugin-clean-css && \ 40 | cd skins/elastic && \ 41 | npx lessc --clean-css="--s1 --advanced" styles/styles.less > styles/styles.min.css && \ 42 | npx lessc --clean-css="--s1 --advanced" styles/print.less > styles/print.min.css && \ 43 | npx lessc --clean-css="--s1 --advanced" styles/embed.less > styles/embed.min.css \ 44 | ) 45 | fi 46 | 47 | # Update cache-buster parameters in CSS-URLs. 48 | bin/updatecss.sh 49 | 50 | # Initialize or update the database. 51 | sudo -u www-data bin/initdb.sh --dir=$PWD/SQL --update || echo "Failed to initialize/update the database. Please start with an empty database and restart the container." 52 | 53 | exec apache2-foreground 54 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Roundcube Docker Examples 2 | 3 | This folder contains a few `docker-compose` files to spin up a Roundcube webmail webserver using the three different variants of images we provide. 4 | 5 | ## Simple Roundcube with Apache and SQLite DB 6 | 7 | See [docker-compose-simple.yaml](./docker-compose-simple.yaml). 8 | 9 | Directly serves Roundcube webmail via HTTP. 10 | The Roundcube sources and the database file are stored on connected volumes. 11 | 12 | ## Standalone Roundcube with Apache using a MySQL DB 13 | 14 | See [docker-compose-mysql.yaml](./docker-compose-mysql.yaml). 15 | 16 | Directly serves Roundcube webmail via HTTP and connects to a MySQL database container. 17 | The Roundcube sources and the database files are stored on connected volumes. 18 | 19 | ## Roundcube served from PHP-FPM via Nginx using a Postgres DB 20 | 21 | See [docker-compose-fpm.yaml](./docker-compose-fpm.yaml) or [docker-compose-fpm-alpine.yaml](./docker-compose-fpm-alpine.yaml). 22 | 23 | An Nginx webserver serves Roundcube from a PHP-FPM container via CGI and static files from the shared Roundcube sources. 24 | A Postgres database container is used to store Roundcube's session and user data. 25 | The Roundcube sources and the database files are stored on connected volumes. 26 | 27 | ## Installing Roundcube Plugins 28 | 29 | With the latest updates, the Roundcube images contain the [Composer](https://getcomposer.org) binary 30 | which is used to install plugins. You can add and activate plugins by executing `composer.phar require ` 31 | inside a running Roundcube container: 32 | 33 | ``` 34 | $ docker exec -it roundcubemail composer require johndoh/contextmenu --update-no-dev 35 | ``` 36 | 37 | If you have mounted the container's volume `/var/www/html` the plugins installed persist on your host system. Otherwise they need to be (re-)installed every time you update or restart the Roundcube container. 38 | 39 | ## Kubernetes Cluster 40 | 41 | The sample [kubernetes.yaml](./kubernetes.yaml) file configures a Roundcube installation on a Kubernetes cluster with three individual deployments and services which can be scaled individually: 42 | 43 | * roundcubedb: Postgres database 44 | * roundcubemail: PHP-FPM with Roundcube 45 | * roundcubenginx: Nginx service serving HTTP 46 | 47 | The setup defines three PersistentVolumeClaims for database and shared temp file storage as well as for sharing the static file of Roundcube with the Nginx pods which finally serve them via HTTP. 48 | 49 | This is only an example and needs to be modified and tweaked for productive systems. At least set the `ROUNDCUBEMAIL_DEFAULT_HOST` and `ROUNDCUBEMAIL_SMTP_SERVER` and change the values of the `roundcubemail-shared-secret` Secret. 50 | -------------------------------------------------------------------------------- /examples/docker-compose-fpm-alpine.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | roundcubemail: 5 | image: roundcube/roundcubemail:latest-fpm-alpine 6 | container_name: roundcubemail 7 | # restart: unless-stopped 8 | depends_on: 9 | - roundcubedb 10 | links: 11 | - roundcubedb 12 | volumes: 13 | - ./www:/var/www/html 14 | environment: 15 | - ROUNDCUBEMAIL_DB_TYPE=pgsql 16 | - ROUNDCUBEMAIL_DB_HOST=roundcubedb # same as pgsql container name 17 | - ROUNDCUBEMAIL_DB_NAME=roundcube # same as pgsql POSTGRES_DB env name 18 | - ROUNDCUBEMAIL_DB_USER=roundcube # same as pgsql POSTGRES_USER env name 19 | - ROUNDCUBEMAIL_DB_PASSWORD=roundcube # same as pgsql POSTGRES_PASSWORD env name 20 | - ROUNDCUBEMAIL_SKIN=elastic 21 | - ROUNDCUBEMAIL_DEFAULT_HOST=tls://mail.example.org 22 | - ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.example.org 23 | 24 | roundcubedb: 25 | image: postgres:alpine 26 | container_name: roundcubedb 27 | # restart: unless-stopped 28 | volumes: 29 | - ./db/postgres:/var/lib/postgresql/data 30 | environment: 31 | - POSTGRES_DB=roundcube 32 | - POSTGRES_USER=roundcube 33 | - POSTGRES_PASSWORD=roundcube 34 | 35 | roundcubenginx: 36 | image: nginx:alpine 37 | container_name: roundcubenginx 38 | # restart: unless-stopped 39 | ports: 40 | - 9008:80 41 | depends_on: 42 | - roundcubemail 43 | links: 44 | - roundcubemail 45 | volumes: 46 | - ./www:/var/www/html 47 | - ./nginx/templates:/etc/nginx/templates 48 | # Provide a custom nginx conf 49 | # - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro 50 | environment: 51 | - NGINX_HOST=localhost # set your local domain or your live domain 52 | - NGINX_PHP_CGI=roundcubemail:9000 # same as roundcubemail container name 53 | 54 | ### Optional: add a full mail server stack to use with Roundcube like https://hub.docker.com/r/tvial/docker-mailserver 55 | # mailserver: 56 | # image: tvial/docker-mailserver:latest 57 | # hostname: mail.example.org 58 | # ... # for more options see https://github.com/tomav/docker-mailserver#examples 59 | -------------------------------------------------------------------------------- /examples/docker-compose-fpm.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | roundcubemail: 5 | image: roundcube/roundcubemail:latest-fpm 6 | container_name: roundcubemail 7 | # restart: unless-stopped 8 | depends_on: 9 | - roundcubedb 10 | links: 11 | - roundcubedb 12 | volumes: 13 | - ./www:/var/www/html 14 | environment: 15 | - ROUNDCUBEMAIL_DB_TYPE=pgsql 16 | - ROUNDCUBEMAIL_DB_HOST=roundcubedb # same as pgsql container name 17 | - ROUNDCUBEMAIL_DB_NAME=roundcube # same as pgsql POSTGRES_DB env name 18 | - ROUNDCUBEMAIL_DB_USER=roundcube # same as pgsql POSTGRES_USER env name 19 | - ROUNDCUBEMAIL_DB_PASSWORD=roundcube # same as pgsql POSTGRES_PASSWORD env name 20 | - ROUNDCUBEMAIL_SKIN=elastic 21 | - ROUNDCUBEMAIL_DEFAULT_HOST=tls://mail.example.org 22 | - ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.example.org 23 | 24 | roundcubedb: 25 | image: postgres:latest 26 | container_name: roundcubedb 27 | # restart: unless-stopped 28 | volumes: 29 | - ./db/postgres:/var/lib/postgresql/data 30 | environment: 31 | - POSTGRES_DB=roundcube 32 | - POSTGRES_USER=roundcube 33 | - POSTGRES_PASSWORD=roundcube 34 | 35 | roundcubenginx: 36 | image: nginx:latest 37 | container_name: roundcubenginx 38 | # restart: unless-stopped 39 | ports: 40 | - 9009:80 41 | depends_on: 42 | - roundcubemail 43 | links: 44 | - roundcubemail 45 | volumes: 46 | - ./www:/var/www/html 47 | - ./nginx/templates:/etc/nginx/templates 48 | # Provide a custom nginx conf 49 | # - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro 50 | environment: 51 | - NGINX_HOST=localhost # set your local domain or your live domain 52 | - NGINX_PHP_CGI=roundcubemail:9000 # same as roundcubemail container name 53 | 54 | ### Optional: add a full mail server stack to use with Roundcube like https://hub.docker.com/r/tvial/docker-mailserver 55 | # mailserver: 56 | # image: tvial/docker-mailserver:latest 57 | # hostname: mail.example.org 58 | # ... # for more options see https://github.com/tomav/docker-mailserver#examples 59 | -------------------------------------------------------------------------------- /examples/docker-compose-mysql.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | roundcubedb: 5 | image: mysql:latest 6 | container_name: roundcubedb 7 | # restart: unless-stopped 8 | volumes: 9 | - ./db/mysql:/var/lib/mysql 10 | environment: 11 | - MYSQL_ROOT_PASSWORD=roundcube-mysql-pw 12 | - MYSQL_DATABASE=roundcubemail 13 | 14 | roundcubemail: 15 | image: roundcube/roundcubemail:latest 16 | container_name: roundcubemail 17 | # restart: unless-stopped 18 | depends_on: 19 | - roundcubedb 20 | links: 21 | - roundcubedb 22 | volumes: 23 | - ./www:/var/www/html 24 | ports: 25 | - 9001:80 26 | environment: 27 | - ROUNDCUBEMAIL_DB_TYPE=mysql 28 | - ROUNDCUBEMAIL_DB_HOST=roundcubedb 29 | - ROUNDCUBEMAIL_DB_PASSWORD=roundcube-mysql-pw 30 | - ROUNDCUBEMAIL_SKIN=elastic 31 | - ROUNDCUBEMAIL_DEFAULT_HOST=tls://mail.example.org 32 | - ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.example.org 33 | 34 | ### Optional: add a full mail server stack to use with Roundcube like https://github.com/docker-mailserver/docker-mailserver 35 | # mailserver: 36 | # image: mailserver/docker-mailserver:latest 37 | # hostname: mail.example.org 38 | # ... # for more options see https://github.com/docker-mailserver/docker-mailserver#examples 39 | -------------------------------------------------------------------------------- /examples/docker-compose-plugins.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | roundcubemail: 5 | image: roundcube/roundcubemail:latest 6 | container_name: roundcubemail 7 | # restart: unless-stopped 8 | volumes: 9 | - ./www:/var/www/html 10 | - ./db/sqlite:/var/roundcube/db 11 | ports: 12 | - 9002:80 13 | environment: 14 | ROUNDCUBEMAIL_DB_TYPE: sqlite 15 | ROUNDCUBEMAIL_SKIN: elastic 16 | ROUNDCUBEMAIL_DEFAULT_HOST: tls://mail.example.org 17 | ROUNDCUBEMAIL_SMTP_SERVER: tls://mail.example.org 18 | ROUNDCUBEMAIL_COMPOSER_PLUGINS: "weird-birds/thunderbird_labels,jfcherng-roundcube/show-folder-size,germancoding/tls_icon:^1.2" 19 | ROUNDCUBEMAIL_PLUGINS: thunderbird_labels, show_folder_size, tls_icon 20 | 21 | ### Optional: add a full mail server stack to use with Roundcube like https://hub.docker.com/r/tvial/docker-mailserver 22 | # mailserver: 23 | # image: tvial/docker-mailserver:latest 24 | # hostname: mail.example.org 25 | # ... # for more options see https://github.com/tomav/docker-mailserver#examples 26 | -------------------------------------------------------------------------------- /examples/docker-compose-simple.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | roundcubemail: 5 | image: roundcube/roundcubemail:latest 6 | container_name: roundcubemail 7 | # restart: unless-stopped 8 | volumes: 9 | - ./www:/var/www/html 10 | - ./db/sqlite:/var/roundcube/db 11 | ports: 12 | - 9002:80 13 | environment: 14 | - ROUNDCUBEMAIL_DB_TYPE=sqlite 15 | - ROUNDCUBEMAIL_SKIN=elastic 16 | - ROUNDCUBEMAIL_DEFAULT_HOST=tls://mail.example.org 17 | - ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.example.org 18 | 19 | ### Optional: add a full mail server stack to use with Roundcube like https://hub.docker.com/r/tvial/docker-mailserver 20 | # mailserver: 21 | # image: tvial/docker-mailserver:latest 22 | # hostname: mail.example.org 23 | # ... # for more options see https://github.com/tomav/docker-mailserver#examples 24 | -------------------------------------------------------------------------------- /examples/kubernetes.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: roundcubemail-www-pvc 5 | spec: 6 | storageClassName: standard 7 | accessModes: 8 | - ReadWriteOnce 9 | resources: 10 | requests: 11 | storage: 200Mi 12 | --- 13 | apiVersion: v1 14 | kind: PersistentVolumeClaim 15 | metadata: 16 | name: roundcubemail-temp-pvc 17 | spec: 18 | storageClassName: standard 19 | accessModes: 20 | - ReadWriteOnce 21 | resources: 22 | requests: 23 | storage: 2Gi 24 | --- 25 | apiVersion: v1 26 | kind: PersistentVolumeClaim 27 | metadata: 28 | name: roundcubedb-volumeclaim 29 | spec: 30 | accessModes: 31 | - ReadWriteOnce 32 | resources: 33 | requests: 34 | storage: 2Gi 35 | --- 36 | apiVersion: v1 37 | kind: Secret 38 | type: Opaque 39 | metadata: 40 | name: roundcubemail-shared-secret 41 | stringData: 42 | DES_KEY: 'a-super-random-value' 43 | DB_USER: roundcube 44 | DB_PASSWORD: roundcubePwd 45 | --- 46 | apiVersion: v1 47 | kind: ConfigMap 48 | metadata: 49 | name: roundcubenginx-config 50 | data: 51 | default.conf: | 52 | server { 53 | listen 80 default_server; 54 | server_name _; 55 | root /var/www/html; 56 | 57 | location / { 58 | try_files $uri /index.php$is_args$args; 59 | } 60 | 61 | location ~ \.php(/|$) { 62 | try_files $uri =404; 63 | fastcgi_pass roundcubemail:9000; 64 | fastcgi_read_timeout 300; 65 | proxy_read_timeout 300; 66 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 67 | include fastcgi_params; 68 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 69 | fastcgi_param DOCUMENT_ROOT $realpath_root; 70 | internal; 71 | } 72 | 73 | client_max_body_size 6m; 74 | 75 | error_log /var/log/nginx/error.log; 76 | access_log /var/log/nginx/access.log; 77 | } 78 | --- 79 | apiVersion: apps/v1 80 | kind: Deployment 81 | metadata: 82 | name: roundcubedb 83 | labels: 84 | service: roundcubedb 85 | spec: 86 | replicas: 1 87 | selector: 88 | matchLabels: 89 | service: roundcubedb 90 | strategy: 91 | type: Recreate 92 | template: 93 | metadata: 94 | labels: 95 | service: roundcubedb 96 | spec: 97 | containers: 98 | - name: roundcubedb 99 | image: postgres:alpine 100 | imagePullPolicy: "" 101 | env: 102 | - name: POSTGRES_DB 103 | value: roundcube 104 | - name: POSTGRES_USER 105 | valueFrom: 106 | secretKeyRef: 107 | name: roundcubemail-shared-secret 108 | key: DB_USER 109 | - name: POSTGRES_PASSWORD 110 | valueFrom: 111 | secretKeyRef: 112 | name: roundcubemail-shared-secret 113 | key: DB_PASSWORD 114 | ports: 115 | - containerPort: 5432 116 | volumeMounts: 117 | - mountPath: /var/lib/postgresql/data 118 | name: roundcubedb-volume 119 | restartPolicy: Always 120 | serviceAccountName: "" 121 | volumes: 122 | - name: roundcubedb-volume 123 | persistentVolumeClaim: 124 | claimName: roundcubedb-volumeclaim 125 | --- 126 | apiVersion: apps/v1 127 | kind: Deployment 128 | metadata: 129 | name: roundcubemail 130 | labels: 131 | service: roundcubemail 132 | spec: 133 | replicas: 1 134 | selector: 135 | matchLabels: 136 | service: roundcubemail 137 | strategy: 138 | type: Recreate 139 | template: 140 | metadata: 141 | labels: 142 | service: roundcubemail 143 | spec: 144 | containers: 145 | - name: roundcubemail 146 | image: roundcube/roundcubemail:latest-fpm-alpine 147 | imagePullPolicy: "" 148 | env: &env 149 | - name: ROUNDCUBEMAIL_DB_TYPE 150 | value: pgsql 151 | - name: ROUNDCUBEMAIL_DB_HOST 152 | value: roundcubedb 153 | - name: ROUNDCUBEMAIL_DB_NAME 154 | value: roundcube 155 | - name: ROUNDCUBEMAIL_DB_USER 156 | valueFrom: 157 | secretKeyRef: 158 | name: roundcubemail-shared-secret 159 | key: DB_USER 160 | - name: ROUNDCUBEMAIL_DB_PASSWORD 161 | valueFrom: 162 | secretKeyRef: 163 | name: roundcubemail-shared-secret 164 | key: DB_PASSWORD 165 | - name: ROUNDCUBEMAIL_DES_KEY 166 | valueFrom: 167 | secretKeyRef: 168 | name: roundcubemail-shared-secret 169 | key: DES_KEY 170 | - name: ROUNDCUBEMAIL_DEFAULT_HOST 171 | value: tls://mail.example.org 172 | - name: ROUNDCUBEMAIL_SMTP_SERVER 173 | value: tls://mail.example.org 174 | - name: ROUNDCUBEMAIL_SKIN 175 | value: elastic 176 | - name: ROUNDCUBEMAIL_PLUGINS 177 | value: archive,zipdownload,newmail_notifier 178 | ports: 179 | - containerPort: 9000 180 | volumeMounts: 181 | - mountPath: /var/www/html 182 | name: www-data 183 | - mountPath: /tmp/roundcube-temp 184 | name: temp-data 185 | restartPolicy: Always 186 | # serviceAccountName: "" 187 | volumes: 188 | - name: www-data 189 | persistentVolumeClaim: 190 | claimName: roundcubemail-www-pvc 191 | - name: temp-data 192 | persistentVolumeClaim: 193 | claimName: roundcubemail-temp-pvc 194 | --- 195 | apiVersion: apps/v1 196 | kind: Deployment 197 | metadata: 198 | name: roundcubenginx 199 | labels: 200 | service: roundcubenginx 201 | spec: 202 | replicas: 1 203 | selector: 204 | matchLabels: 205 | service: roundcubenginx 206 | strategy: 207 | type: Recreate 208 | template: 209 | metadata: 210 | labels: 211 | service: roundcubenginx 212 | spec: 213 | containers: 214 | - name: roundcubenginx 215 | image: nginx:alpine 216 | imagePullPolicy: "" 217 | env: 218 | - name: NGINX_HOST 219 | value: localhost 220 | - name: NGINX_PHP_CGI 221 | value: roundcubemail:9000 222 | ports: 223 | - containerPort: 80 224 | volumeMounts: 225 | - name: www-data 226 | mountPath: /var/www/html 227 | - name: nginx-config 228 | mountPath: /etc/nginx/conf.d/default.conf 229 | subPath: default.conf 230 | restartPolicy: Always 231 | serviceAccountName: "" 232 | volumes: 233 | - name: www-data 234 | persistentVolumeClaim: 235 | claimName: roundcubemail-www-pvc 236 | - name: nginx-config 237 | configMap: 238 | name: roundcubenginx-config 239 | --- 240 | apiVersion: v1 241 | kind: Service 242 | metadata: 243 | name: roundcubedb 244 | labels: 245 | service: roundcubedb 246 | spec: 247 | type: NodePort 248 | ports: 249 | - port: 5432 250 | protocol: TCP 251 | selector: 252 | service: roundcubedb 253 | --- 254 | apiVersion: v1 255 | kind: Service 256 | metadata: 257 | name: roundcubemail 258 | labels: 259 | service: roundcubemail 260 | spec: 261 | type: NodePort 262 | ports: 263 | - port: 9000 264 | protocol: TCP 265 | selector: 266 | service: roundcubemail 267 | --- 268 | apiVersion: v1 269 | kind: Service 270 | metadata: 271 | name: roundcubenginx 272 | labels: 273 | service: roundcubenginx 274 | spec: 275 | ports: 276 | - name: http 277 | port: 8080 278 | targetPort: 80 279 | selector: 280 | service: roundcubenginx 281 | --- 282 | apiVersion: batch/v1 283 | kind: CronJob 284 | metadata: 285 | name: cleandb 286 | spec: 287 | schedule: "@daily" 288 | concurrencyPolicy: Replace 289 | jobTemplate: 290 | spec: 291 | template: 292 | metadata: 293 | name: cleandb 294 | spec: 295 | restartPolicy: OnFailure 296 | containers: 297 | - name: roundcubemail 298 | image: roundcube/roundcubemail:latest-fpm-alpine 299 | imagePullPolicy: "" 300 | env: *env 301 | args: 302 | - bin/cleandb.sh 303 | -------------------------------------------------------------------------------- /examples/nginx/templates/default.conf.template: -------------------------------------------------------------------------------- 1 | server { 2 | index index.php index.html; 3 | server_name php-docker.local; 4 | error_log /var/log/nginx/error.log; 5 | access_log /var/log/nginx/access.log; 6 | root /var/www/html/public_html; 7 | 8 | location ~ /(temp|logs)/ { 9 | deny all; 10 | return 403; 11 | } 12 | 13 | location ~ \.php$ { 14 | try_files $uri =404; 15 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 16 | fastcgi_pass ${NGINX_PHP_CGI}; 17 | fastcgi_index index.php; 18 | include fastcgi_params; 19 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 20 | fastcgi_param PATH_INFO $fastcgi_path_info; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fpm-alpine/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-fpm-alpine as root 2 | LABEL maintainer="Thomas Bruederli " 3 | LABEL org.opencontainers.image.source="https://github.com/roundcube/roundcubemail-docker" 4 | 5 | # This should be done by the upstream images, but as long as they don't do it, 6 | # we rather use our own hands than suffer from outdated packages. 7 | # Kept as standalone command to make it stand out and be easy to remove. 8 | RUN apk upgrade --no-cache 9 | 10 | RUN set -ex; \ 11 | if [ "fpm-alpine" = "apache" ]; then a2enmod rewrite; fi; \ 12 | \ 13 | apk add --no-cache \ 14 | bash \ 15 | coreutils \ 16 | rsync \ 17 | tzdata \ 18 | aspell \ 19 | aspell-en \ 20 | unzip 21 | 22 | RUN set -ex; \ 23 | \ 24 | apk add --no-cache --virtual .build-deps \ 25 | $PHPIZE_DEPS \ 26 | icu-dev \ 27 | freetype-dev \ 28 | imagemagick-dev \ 29 | libjpeg-turbo-dev \ 30 | libpng-dev \ 31 | libzip-dev \ 32 | libtool \ 33 | openldap-dev \ 34 | postgresql-dev \ 35 | sqlite-dev \ 36 | aspell-dev \ 37 | ; \ 38 | \ 39 | # Extract sources to avoid using pecl (https://github.com/docker-library/php/issues/374#issuecomment-690698974) 40 | pecl bundle -d /usr/src/php/ext imagick; \ 41 | pecl bundle -d /usr/src/php/ext redis; \ 42 | docker-php-ext-configure gd --with-jpeg --with-freetype; \ 43 | docker-php-ext-configure ldap; \ 44 | docker-php-ext-install \ 45 | exif \ 46 | gd \ 47 | intl \ 48 | ldap \ 49 | pdo_mysql \ 50 | pdo_pgsql \ 51 | pdo_sqlite \ 52 | zip \ 53 | pspell \ 54 | imagick \ 55 | redis \ 56 | ; \ 57 | docker-php-ext-enable imagick opcache redis; \ 58 | docker-php-source delete; \ 59 | rm -r /tmp/pear; \ 60 | # Display installed modules 61 | php -m; \ 62 | \ 63 | extdir="$(php -r 'echo ini_get("extension_dir");')"; \ 64 | runDeps="$( \ 65 | scanelf --needed --nobanner --format '%n#p' --recursive $extdir \ 66 | | tr ',' '\n' \ 67 | | sort -u \ 68 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 69 | )"; \ 70 | apk add --virtual .roundcubemail-phpext-rundeps imagemagick $runDeps; \ 71 | apk del .build-deps; \ 72 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 73 | [ -z "$err" ] || (echo "Sanity check failed: php returned errors; $err"; exit 1;); \ 74 | # include the wait-for-it.sh script (latest commit) 75 | curl -fL https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh -o /wait-for-it.sh; \ 76 | chmod +x /wait-for-it.sh; 77 | 78 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 79 | 80 | # Use the default production configuration 81 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 82 | 83 | # use custom PHP settings 84 | COPY php.ini /usr/local/etc/php/conf.d/roundcube-defaults.ini 85 | 86 | COPY --chmod=0755 docker-entrypoint.sh / 87 | 88 | # Define Roundcubemail version 89 | ENV ROUNDCUBEMAIL_VERSION 1.6.11 90 | 91 | # Define the GPG key used for the bundle verification process 92 | ENV ROUNDCUBEMAIL_KEYID "F3E4 C04B B3DB 5D42 15C4 5F7F 5AB2 BAA1 41C4 F7D5" 93 | 94 | # Download package and extract to web volume 95 | RUN set -ex; \ 96 | apk add --no-cache gnupg; \ 97 | curl -o roundcubemail.tar.gz -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz; \ 98 | curl -o roundcubemail.tar.gz.asc -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz.asc; \ 99 | export GNUPGHOME="$(mktemp -d)"; \ 100 | curl -fSL https://roundcube.net/download/pubkey.asc -o /tmp/pubkey.asc; \ 101 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o 'Key fingerprint') != 1 ]; then echo 'The key file should contain only one GPG key'; exit 1; fi; \ 102 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o "${ROUNDCUBEMAIL_KEYID}") != 1 ]; then echo 'The key ID should be the roundcube one'; exit 1; fi; \ 103 | gpg --batch --import /tmp/pubkey.asc; \ 104 | rm /tmp/pubkey.asc; \ 105 | gpg --batch --verify roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 106 | gpgconf --kill all; \ 107 | mkdir /usr/src/roundcubemail; \ 108 | tar -xf roundcubemail.tar.gz -C /usr/src/roundcubemail --strip-components=1 --no-same-owner; \ 109 | rm -r "$GNUPGHOME" roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 110 | rm -rf /usr/src/roundcubemail/installer; \ 111 | chown -R www-data:www-data /usr/src/roundcubemail/logs; \ 112 | # Create the config dir 113 | mkdir -p /var/roundcube/config /var/roundcube/enigma; \ 114 | chown -R www-data:www-data /var/roundcube; \ 115 | chmod +t /var/roundcube 116 | 117 | ENTRYPOINT ["/docker-entrypoint.sh"] 118 | CMD ["php-fpm"] 119 | 120 | 121 | #### non-root stage 122 | 123 | FROM root as nonroot 124 | 125 | USER 82:82 -------------------------------------------------------------------------------- /fpm-alpine/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -ex 3 | 4 | # PWD=`pwd` 5 | 6 | if [[ "$1" == apache2* || "$1" == php-fpm || "$1" == bin* ]]; then 7 | INSTALLDIR=`pwd` 8 | # docroot is empty 9 | if ! [ -e index.php -a -e bin/installto.sh ]; then 10 | echo >&2 "roundcubemail not found in $PWD - copying now..." 11 | if [ "$(ls -A)" ]; then 12 | echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" 13 | ( set -x; ls -A; sleep 10 ) 14 | fi 15 | tar cf - --one-file-system -C /usr/src/roundcubemail . | tar xf - 16 | echo >&2 "Complete! ROUNDCUBEMAIL has been successfully copied to $INSTALLDIR" 17 | # update Roundcube in docroot 18 | else 19 | echo >&2 "roundcubemail found in $INSTALLDIR - installing update..." 20 | (cd /usr/src/roundcubemail && bin/installto.sh -y $INSTALLDIR) 21 | # Re-install composer modules (including plugins) 22 | composer \ 23 | --working-dir=${INSTALLDIR} \ 24 | --prefer-dist \ 25 | --no-dev \ 26 | --no-interaction \ 27 | --optimize-autoloader \ 28 | install 29 | fi 30 | 31 | if [ -f /run/secrets/roundcube_db_user ]; then 32 | ROUNDCUBEMAIL_DB_USER=`cat /run/secrets/roundcube_db_user` 33 | fi 34 | if [ -f /run/secrets/roundcube_db_password ]; then 35 | ROUNDCUBEMAIL_DB_PASSWORD=`cat /run/secrets/roundcube_db_password` 36 | fi 37 | if [ -f /run/secrets/roundcube_oauth_client_secret ]; then 38 | ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET=`cat /run/secrets/roundcube_oauth_client_secret` 39 | fi 40 | 41 | if [ ! -z "${!POSTGRES_ENV_POSTGRES_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "pgsql" ]; then 42 | : "${ROUNDCUBEMAIL_DB_TYPE:=pgsql}" 43 | : "${ROUNDCUBEMAIL_DB_HOST:=postgres}" 44 | : "${ROUNDCUBEMAIL_DB_PORT:=5432}" 45 | : "${ROUNDCUBEMAIL_DB_USER:=${POSTGRES_ENV_POSTGRES_USER}}" 46 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${POSTGRES_ENV_POSTGRES_PASSWORD}}" 47 | : "${ROUNDCUBEMAIL_DB_NAME:=${POSTGRES_ENV_POSTGRES_DB:-roundcubemail}}" 48 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 49 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 50 | # DO NOT USE wait-for-it.sh 51 | else 52 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 53 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 54 | fi 55 | elif [ ! -z "${!MYSQL_ENV_MYSQL_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "mysql" ]; then 56 | : "${ROUNDCUBEMAIL_DB_TYPE:=mysql}" 57 | : "${ROUNDCUBEMAIL_DB_HOST:=mysql}" 58 | : "${ROUNDCUBEMAIL_DB_PORT:=3306}" 59 | : "${ROUNDCUBEMAIL_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" 60 | if [ "$ROUNDCUBEMAIL_DB_USER" = 'root' ]; then 61 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD}}" 62 | else 63 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD}}" 64 | fi 65 | : "${ROUNDCUBEMAIL_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-roundcubemail}}" 66 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 67 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 68 | # DO NOT USE wait-for-it.sh 69 | else 70 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 71 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 72 | fi 73 | else 74 | # use local SQLite DB in /var/roundcube/db 75 | : "${ROUNDCUBEMAIL_DB_TYPE:=sqlite}" 76 | : "${ROUNDCUBEMAIL_DB_DIR:=/var/roundcube/db}" 77 | : "${ROUNDCUBEMAIL_DB_NAME:=sqlite}" 78 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}:///$ROUNDCUBEMAIL_DB_DIR/${ROUNDCUBEMAIL_DB_NAME}.db?mode=0646}" 79 | 80 | mkdir -p $ROUNDCUBEMAIL_DB_DIR 81 | chown www-data:www-data $ROUNDCUBEMAIL_DB_DIR 82 | fi 83 | 84 | : "${ROUNDCUBEMAIL_DEFAULT_HOST:=localhost}" 85 | : "${ROUNDCUBEMAIL_DEFAULT_PORT:=143}" 86 | : "${ROUNDCUBEMAIL_SMTP_SERVER:=localhost}" 87 | : "${ROUNDCUBEMAIL_SMTP_PORT:=587}" 88 | : "${ROUNDCUBEMAIL_PLUGINS:=archive,zipdownload}" 89 | : "${ROUNDCUBEMAIL_SKIN:=elastic}" 90 | : "${ROUNDCUBEMAIL_TEMP_DIR:=/tmp/roundcube-temp}" 91 | : "${ROUNDCUBEMAIL_REQUEST_PATH:=/}" 92 | : "${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER:=$INSTALLDIR}" 93 | 94 | if [ ! -z "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" ]; then 95 | echo "Installing plugins from the list" 96 | echo "Plugins: ${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" 97 | 98 | # Change ',' into a space 99 | ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH=`echo "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" | tr ',' ' '` 100 | 101 | composer \ 102 | --working-dir=${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER} \ 103 | --prefer-dist \ 104 | --prefer-stable \ 105 | --update-no-dev \ 106 | --no-interaction \ 107 | --optimize-autoloader \ 108 | require \ 109 | -- \ 110 | ${ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH}; 111 | fi 112 | 113 | if [ ! -d skins/${ROUNDCUBEMAIL_SKIN} ]; then 114 | # Installing missing skin 115 | echo "Installing missing skin: ${ROUNDCUBEMAIL_SKIN}" 116 | composer \ 117 | --working-dir=${INSTALLDIR} \ 118 | --prefer-dist \ 119 | --prefer-stable \ 120 | --update-no-dev \ 121 | --no-interaction \ 122 | --optimize-autoloader \ 123 | require \ 124 | -- \ 125 | roundcube/${ROUNDCUBEMAIL_SKIN}; 126 | fi 127 | 128 | if [ ! -e config/config.inc.php ]; then 129 | GENERATED_DES_KEY=`head /dev/urandom | base64 | head -c 24` 130 | touch config/config.inc.php 131 | 132 | echo "Write root config to $PWD/config/config.inc.php" 133 | echo " config/config.inc.php 142 | 143 | elif ! grep -q "config.docker.inc.php" config/config.inc.php; then 144 | echo "include(__DIR__ . '/config.docker.inc.php');" >> config/config.inc.php 145 | fi 146 | 147 | ROUNDCUBEMAIL_PLUGINS_PHP=`echo "${ROUNDCUBEMAIL_PLUGINS}" | sed -E "s/[, ]+/', '/g"` 148 | echo "Write Docker config to $PWD/config/config.docker.inc.php" 149 | echo " config/config.docker.inc.php 160 | 161 | if [ -e /run/secrets/roundcube_des_key ]; then 162 | echo "\$config['des_key'] = file_get_contents('/run/secrets/roundcube_des_key');" >> config/config.docker.inc.php 163 | elif [ ! -z "${ROUNDCUBEMAIL_DES_KEY}" ]; then 164 | echo "\$config['des_key'] = getenv('ROUNDCUBEMAIL_DES_KEY');" >> config/config.docker.inc.php 165 | fi 166 | 167 | if [ ! -z "${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}" ]; then 168 | echo "\$config['oauth_client_secret'] = '${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}';" >> config/config.docker.inc.php 169 | fi 170 | 171 | if [ ! -z "${ROUNDCUBEMAIL_SPELLCHECK_URI}" ]; then 172 | echo "\$config['spellcheck_engine'] = 'googie';" >> config/config.docker.inc.php 173 | echo "\$config['spellcheck_uri'] = '${ROUNDCUBEMAIL_SPELLCHECK_URI}';" >> config/config.docker.inc.php 174 | fi 175 | 176 | # If the "enigma" plugin is enabled but has no storage configured, inject a default value for the mandatory setting. 177 | if $(echo $ROUNDCUBEMAIL_PLUGINS | grep -Eq '\benigma\b') && ! grep -qr enigma_pgp_homedir /var/roundcube/config/; then 178 | echo "\$config['enigma_pgp_homedir'] = '/var/roundcube/enigma';" >> config/config.docker.inc.php 179 | fi 180 | 181 | # include custom config files 182 | for fn in `ls /var/roundcube/config/*.php 2>/dev/null || true`; do 183 | echo "include('$fn');" >> config/config.docker.inc.php 184 | done 185 | 186 | # initialize or update DB 187 | bin/initdb.sh --dir=$PWD/SQL --update || echo "Failed to initialize/update the database. Please start with an empty database and restart the container." 188 | 189 | if [ ! -z "${ROUNDCUBEMAIL_TEMP_DIR}" ]; then 190 | mkdir -p ${ROUNDCUBEMAIL_TEMP_DIR} && chown www-data ${ROUNDCUBEMAIL_TEMP_DIR} 191 | fi 192 | 193 | if [ ! -z "${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" ]; then 194 | echo "upload_max_filesize=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 195 | echo "post_max_size=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 196 | fi 197 | 198 | : "${ROUNDCUBEMAIL_LOCALE:=en_US.UTF-8 UTF-8}" 199 | 200 | if [ -e /usr/sbin/locale-gen ] && [ ! -f /etc/locale.gen ] && [ ! -z "${ROUNDCUBEMAIL_LOCALE}" ]; then 201 | echo "${ROUNDCUBEMAIL_LOCALE}" > /etc/locale.gen && /usr/sbin/locale-gen 202 | fi 203 | 204 | if [ ! -z "${ROUNDCUBEMAIL_ASPELL_DICTS}" ]; then 205 | ASPELL_PACKAGES=`echo -n "aspell-${ROUNDCUBEMAIL_ASPELL_DICTS}" | sed -E "s/[, ]+/ aspell-/g"` 206 | which apt-get && apt-get update && apt-get install -y $ASPELL_PACKAGES 207 | which apk && apk add --no-cache $ASPELL_PACKAGES 208 | fi 209 | 210 | fi 211 | 212 | exec "$@" 213 | -------------------------------------------------------------------------------- /fpm-alpine/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=64M 2 | display_errors=Off 3 | log_errors=On 4 | upload_max_filesize=5M 5 | post_max_size=6M 6 | zlib.output_compression=Off 7 | session.auto_start=Off 8 | session.gc_maxlifetime=21600 9 | session.gc_divisor=500 10 | session.gc_probability=1 11 | -------------------------------------------------------------------------------- /fpm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.1-fpm as root 2 | LABEL maintainer="Thomas Bruederli " 3 | LABEL org.opencontainers.image.source="https://github.com/roundcube/roundcubemail-docker" 4 | 5 | # This should be done by the upstream images, but as long as they don't do it, 6 | # we rather use our own hands than suffer from outdated packages. 7 | # Kept as standalone command to make it stand out and be easy to remove. 8 | RUN apt-get update && apt-get -y upgrade && apt-get clean 9 | 10 | RUN set -ex; \ 11 | if [ "fpm" = "apache" ]; then \ 12 | a2enmod rewrite; \ 13 | # Make Apache use public_html/ as document root to protect files outside of it \ 14 | # against unauthorized access. \ 15 | # This is possible and recommended since a while, and will be required for Roundcubemail v1.7. \ 16 | sed -i -e 's|\(DocumentRoot /var/www/html\)$|\1/public_html|' /etc/apache2/sites-available/000-default.conf; \ 17 | fi; \ 18 | apt-get update; \ 19 | \ 20 | savedAptMark="$(apt-mark showmanual)"; \ 21 | \ 22 | apt-get install -y --no-install-recommends \ 23 | libfreetype6-dev \ 24 | libicu-dev \ 25 | libjpeg62-turbo-dev \ 26 | libldap2-dev \ 27 | libmagickwand-dev \ 28 | libpng-dev \ 29 | libpq-dev \ 30 | libsqlite3-dev \ 31 | libzip-dev \ 32 | libpspell-dev \ 33 | libonig-dev \ 34 | libldap-common \ 35 | ; \ 36 | # installto.sh & web install dependencies 37 | fetchDeps="gnupg locales libc-l10n"; \ 38 | installDeps="aspell aspell-en rsync unzip"; \ 39 | apt-get install -y --no-install-recommends \ 40 | $installDeps \ 41 | $fetchDeps \ 42 | ; \ 43 | \ 44 | # Extract sources to avoid using pecl (https://github.com/docker-library/php/issues/374#issuecomment-690698974) 45 | pecl bundle -d /usr/src/php/ext imagick; \ 46 | pecl bundle -d /usr/src/php/ext redis; \ 47 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 48 | docker-php-ext-configure gd --with-jpeg --with-freetype; \ 49 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 50 | docker-php-ext-install \ 51 | exif \ 52 | gd \ 53 | intl \ 54 | ldap \ 55 | pdo_mysql \ 56 | pdo_pgsql \ 57 | pdo_sqlite \ 58 | zip \ 59 | pspell \ 60 | imagick \ 61 | redis \ 62 | ; \ 63 | docker-php-ext-enable imagick opcache redis; \ 64 | docker-php-source delete; \ 65 | rm -r /tmp/pear; \ 66 | # Display installed modules 67 | php -m; \ 68 | \ 69 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 70 | apt-mark auto '.*' > /dev/null; \ 71 | apt-mark manual $savedAptMark $installDeps $fetchDeps; \ 72 | extdir="$(php -r 'echo ini_get("extension_dir");')"; \ 73 | ldd "$extdir"/*.so \ 74 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 75 | | sort -u \ 76 | | xargs -r dpkg-query -S \ 77 | | cut -d: -f1 \ 78 | | sort -u \ 79 | | xargs -rt apt-mark manual; \ 80 | \ 81 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 82 | rm -rf /var/lib/apt/lists/*; \ 83 | ldd "$extdir"/*.so | grep -qzv "=> not found" || (echo "Sanity check failed: missing libraries:"; ldd "$extdir"/*.so | grep " => not found"; exit 1); \ 84 | ldd "$extdir"/*.so | grep -q "libzip.so.* => .*/libzip.so.*" || (echo "Sanity check failed: libzip.so is not referenced"; ldd "$extdir"/*.so; exit 1); \ 85 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 86 | [ -z "$err" ] || (echo "Sanity check failed: php returned errors; $err"; exit 1;); \ 87 | # include the wait-for-it.sh script (latest commit) 88 | curl -fL https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh -o /wait-for-it.sh; \ 89 | chmod +x /wait-for-it.sh; 90 | 91 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 92 | 93 | # Use the default production configuration 94 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 95 | 96 | # use custom PHP settings 97 | COPY php.ini /usr/local/etc/php/conf.d/roundcube-defaults.ini 98 | 99 | COPY --chmod=0755 docker-entrypoint.sh / 100 | 101 | # Define Roundcubemail version 102 | ENV ROUNDCUBEMAIL_VERSION 1.6.11 103 | 104 | # Define the GPG key used for the bundle verification process 105 | ENV ROUNDCUBEMAIL_KEYID "F3E4 C04B B3DB 5D42 15C4 5F7F 5AB2 BAA1 41C4 F7D5" 106 | 107 | # Download package and extract to web volume 108 | RUN set -ex; \ 109 | curl -o roundcubemail.tar.gz -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz; \ 110 | curl -o roundcubemail.tar.gz.asc -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz.asc; \ 111 | export GNUPGHOME="$(mktemp -d)"; \ 112 | curl -fSL https://roundcube.net/download/pubkey.asc -o /tmp/pubkey.asc; \ 113 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o 'Key fingerprint') != 1 ]; then echo 'The key file should contain only one GPG key'; exit 1; fi; \ 114 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o "${ROUNDCUBEMAIL_KEYID}") != 1 ]; then echo 'The key ID should be the roundcube one'; exit 1; fi; \ 115 | gpg --batch --import /tmp/pubkey.asc; \ 116 | rm /tmp/pubkey.asc; \ 117 | gpg --batch --verify roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 118 | gpgconf --kill all; \ 119 | mkdir /usr/src/roundcubemail; \ 120 | tar -xf roundcubemail.tar.gz -C /usr/src/roundcubemail --strip-components=1 --no-same-owner; \ 121 | rm -r "$GNUPGHOME" roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 122 | rm -rf /usr/src/roundcubemail/installer; \ 123 | chown -R www-data:www-data /usr/src/roundcubemail/logs; \ 124 | # Create the config dir 125 | mkdir -p /var/roundcube/config /var/roundcube/enigma; \ 126 | chown -R www-data:www-data /var/roundcube; \ 127 | chmod +t /var/roundcube 128 | 129 | ENTRYPOINT ["/docker-entrypoint.sh"] 130 | CMD ["php-fpm"] 131 | 132 | 133 | #### non-root stage 134 | 135 | FROM root as nonroot 136 | 137 | # Prepare locale config for locale-gen 138 | RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen; \ 139 | /usr/sbin/locale-gen 140 | 141 | 142 | 143 | USER 33:33 -------------------------------------------------------------------------------- /fpm/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -ex 3 | 4 | # PWD=`pwd` 5 | 6 | if [[ "$1" == apache2* || "$1" == php-fpm || "$1" == bin* ]]; then 7 | INSTALLDIR=`pwd` 8 | # docroot is empty 9 | if ! [ -e index.php -a -e bin/installto.sh ]; then 10 | echo >&2 "roundcubemail not found in $PWD - copying now..." 11 | if [ "$(ls -A)" ]; then 12 | echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" 13 | ( set -x; ls -A; sleep 10 ) 14 | fi 15 | tar cf - --one-file-system -C /usr/src/roundcubemail . | tar xf - 16 | echo >&2 "Complete! ROUNDCUBEMAIL has been successfully copied to $INSTALLDIR" 17 | # update Roundcube in docroot 18 | else 19 | echo >&2 "roundcubemail found in $INSTALLDIR - installing update..." 20 | (cd /usr/src/roundcubemail && bin/installto.sh -y $INSTALLDIR) 21 | # Re-install composer modules (including plugins) 22 | composer \ 23 | --working-dir=${INSTALLDIR} \ 24 | --prefer-dist \ 25 | --no-dev \ 26 | --no-interaction \ 27 | --optimize-autoloader \ 28 | install 29 | fi 30 | 31 | if [ -f /run/secrets/roundcube_db_user ]; then 32 | ROUNDCUBEMAIL_DB_USER=`cat /run/secrets/roundcube_db_user` 33 | fi 34 | if [ -f /run/secrets/roundcube_db_password ]; then 35 | ROUNDCUBEMAIL_DB_PASSWORD=`cat /run/secrets/roundcube_db_password` 36 | fi 37 | if [ -f /run/secrets/roundcube_oauth_client_secret ]; then 38 | ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET=`cat /run/secrets/roundcube_oauth_client_secret` 39 | fi 40 | 41 | if [ ! -z "${!POSTGRES_ENV_POSTGRES_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "pgsql" ]; then 42 | : "${ROUNDCUBEMAIL_DB_TYPE:=pgsql}" 43 | : "${ROUNDCUBEMAIL_DB_HOST:=postgres}" 44 | : "${ROUNDCUBEMAIL_DB_PORT:=5432}" 45 | : "${ROUNDCUBEMAIL_DB_USER:=${POSTGRES_ENV_POSTGRES_USER}}" 46 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${POSTGRES_ENV_POSTGRES_PASSWORD}}" 47 | : "${ROUNDCUBEMAIL_DB_NAME:=${POSTGRES_ENV_POSTGRES_DB:-roundcubemail}}" 48 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 49 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 50 | # DO NOT USE wait-for-it.sh 51 | else 52 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 53 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 54 | fi 55 | elif [ ! -z "${!MYSQL_ENV_MYSQL_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "mysql" ]; then 56 | : "${ROUNDCUBEMAIL_DB_TYPE:=mysql}" 57 | : "${ROUNDCUBEMAIL_DB_HOST:=mysql}" 58 | : "${ROUNDCUBEMAIL_DB_PORT:=3306}" 59 | : "${ROUNDCUBEMAIL_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" 60 | if [ "$ROUNDCUBEMAIL_DB_USER" = 'root' ]; then 61 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD}}" 62 | else 63 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD}}" 64 | fi 65 | : "${ROUNDCUBEMAIL_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-roundcubemail}}" 66 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 67 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 68 | # DO NOT USE wait-for-it.sh 69 | else 70 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 71 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 72 | fi 73 | else 74 | # use local SQLite DB in /var/roundcube/db 75 | : "${ROUNDCUBEMAIL_DB_TYPE:=sqlite}" 76 | : "${ROUNDCUBEMAIL_DB_DIR:=/var/roundcube/db}" 77 | : "${ROUNDCUBEMAIL_DB_NAME:=sqlite}" 78 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}:///$ROUNDCUBEMAIL_DB_DIR/${ROUNDCUBEMAIL_DB_NAME}.db?mode=0646}" 79 | 80 | mkdir -p $ROUNDCUBEMAIL_DB_DIR 81 | chown www-data:www-data $ROUNDCUBEMAIL_DB_DIR 82 | fi 83 | 84 | : "${ROUNDCUBEMAIL_DEFAULT_HOST:=localhost}" 85 | : "${ROUNDCUBEMAIL_DEFAULT_PORT:=143}" 86 | : "${ROUNDCUBEMAIL_SMTP_SERVER:=localhost}" 87 | : "${ROUNDCUBEMAIL_SMTP_PORT:=587}" 88 | : "${ROUNDCUBEMAIL_PLUGINS:=archive,zipdownload}" 89 | : "${ROUNDCUBEMAIL_SKIN:=elastic}" 90 | : "${ROUNDCUBEMAIL_TEMP_DIR:=/tmp/roundcube-temp}" 91 | : "${ROUNDCUBEMAIL_REQUEST_PATH:=/}" 92 | : "${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER:=$INSTALLDIR}" 93 | 94 | if [ ! -z "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" ]; then 95 | echo "Installing plugins from the list" 96 | echo "Plugins: ${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" 97 | 98 | # Change ',' into a space 99 | ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH=`echo "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" | tr ',' ' '` 100 | 101 | composer \ 102 | --working-dir=${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER} \ 103 | --prefer-dist \ 104 | --prefer-stable \ 105 | --update-no-dev \ 106 | --no-interaction \ 107 | --optimize-autoloader \ 108 | require \ 109 | -- \ 110 | ${ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH}; 111 | fi 112 | 113 | if [ ! -d skins/${ROUNDCUBEMAIL_SKIN} ]; then 114 | # Installing missing skin 115 | echo "Installing missing skin: ${ROUNDCUBEMAIL_SKIN}" 116 | composer \ 117 | --working-dir=${INSTALLDIR} \ 118 | --prefer-dist \ 119 | --prefer-stable \ 120 | --update-no-dev \ 121 | --no-interaction \ 122 | --optimize-autoloader \ 123 | require \ 124 | -- \ 125 | roundcube/${ROUNDCUBEMAIL_SKIN}; 126 | fi 127 | 128 | if [ ! -e config/config.inc.php ]; then 129 | GENERATED_DES_KEY=`head /dev/urandom | base64 | head -c 24` 130 | touch config/config.inc.php 131 | 132 | echo "Write root config to $PWD/config/config.inc.php" 133 | echo " config/config.inc.php 142 | 143 | elif ! grep -q "config.docker.inc.php" config/config.inc.php; then 144 | echo "include(__DIR__ . '/config.docker.inc.php');" >> config/config.inc.php 145 | fi 146 | 147 | ROUNDCUBEMAIL_PLUGINS_PHP=`echo "${ROUNDCUBEMAIL_PLUGINS}" | sed -E "s/[, ]+/', '/g"` 148 | echo "Write Docker config to $PWD/config/config.docker.inc.php" 149 | echo " config/config.docker.inc.php 160 | 161 | if [ -e /run/secrets/roundcube_des_key ]; then 162 | echo "\$config['des_key'] = file_get_contents('/run/secrets/roundcube_des_key');" >> config/config.docker.inc.php 163 | elif [ ! -z "${ROUNDCUBEMAIL_DES_KEY}" ]; then 164 | echo "\$config['des_key'] = getenv('ROUNDCUBEMAIL_DES_KEY');" >> config/config.docker.inc.php 165 | fi 166 | 167 | if [ ! -z "${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}" ]; then 168 | echo "\$config['oauth_client_secret'] = '${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}';" >> config/config.docker.inc.php 169 | fi 170 | 171 | if [ ! -z "${ROUNDCUBEMAIL_SPELLCHECK_URI}" ]; then 172 | echo "\$config['spellcheck_engine'] = 'googie';" >> config/config.docker.inc.php 173 | echo "\$config['spellcheck_uri'] = '${ROUNDCUBEMAIL_SPELLCHECK_URI}';" >> config/config.docker.inc.php 174 | fi 175 | 176 | # If the "enigma" plugin is enabled but has no storage configured, inject a default value for the mandatory setting. 177 | if $(echo $ROUNDCUBEMAIL_PLUGINS | grep -Eq '\benigma\b') && ! grep -qr enigma_pgp_homedir /var/roundcube/config/; then 178 | echo "\$config['enigma_pgp_homedir'] = '/var/roundcube/enigma';" >> config/config.docker.inc.php 179 | fi 180 | 181 | # include custom config files 182 | for fn in `ls /var/roundcube/config/*.php 2>/dev/null || true`; do 183 | echo "include('$fn');" >> config/config.docker.inc.php 184 | done 185 | 186 | # initialize or update DB 187 | bin/initdb.sh --dir=$PWD/SQL --update || echo "Failed to initialize/update the database. Please start with an empty database and restart the container." 188 | 189 | if [ ! -z "${ROUNDCUBEMAIL_TEMP_DIR}" ]; then 190 | mkdir -p ${ROUNDCUBEMAIL_TEMP_DIR} && chown www-data ${ROUNDCUBEMAIL_TEMP_DIR} 191 | fi 192 | 193 | if [ ! -z "${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" ]; then 194 | echo "upload_max_filesize=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 195 | echo "post_max_size=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 196 | fi 197 | 198 | : "${ROUNDCUBEMAIL_LOCALE:=en_US.UTF-8 UTF-8}" 199 | 200 | if [ -e /usr/sbin/locale-gen ] && [ ! -f /etc/locale.gen ] && [ ! -z "${ROUNDCUBEMAIL_LOCALE}" ]; then 201 | echo "${ROUNDCUBEMAIL_LOCALE}" > /etc/locale.gen && /usr/sbin/locale-gen 202 | fi 203 | 204 | if [ ! -z "${ROUNDCUBEMAIL_ASPELL_DICTS}" ]; then 205 | ASPELL_PACKAGES=`echo -n "aspell-${ROUNDCUBEMAIL_ASPELL_DICTS}" | sed -E "s/[, ]+/ aspell-/g"` 206 | which apt-get && apt-get update && apt-get install -y $ASPELL_PACKAGES 207 | which apk && apk add --no-cache $ASPELL_PACKAGES 208 | fi 209 | 210 | fi 211 | 212 | exec "$@" 213 | -------------------------------------------------------------------------------- /fpm/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=64M 2 | display_errors=Off 3 | log_errors=On 4 | upload_max_filesize=5M 5 | post_max_size=6M 6 | zlib.output_compression=Off 7 | session.auto_start=Off 8 | session.gc_maxlifetime=21600 9 | session.gc_divisor=500 10 | session.gc_probability=1 11 | -------------------------------------------------------------------------------- /nightly/Dockerfile: -------------------------------------------------------------------------------- 1 | ### Temporary build container to use npm in. 2 | FROM docker.io/roundcube/roundcubemail:latest-apache AS builder 3 | 4 | # install nodejs and lessc compiler 5 | RUN apt-get -qq update && apt-get install -y --no-install-recommends npm git 6 | 7 | # Download source and build package into src directory 8 | RUN set -ex; \ 9 | rm -rf /usr/src/roundcubemail; \ 10 | curl -o roundcubemail.tar.gz -SL https://github.com/roundcube/roundcubemail/archive/master.tar.gz; \ 11 | tar -xzf roundcubemail.tar.gz -C /usr/src/; \ 12 | rm roundcubemail.tar.gz; \ 13 | mv /usr/src/roundcubemail-master /usr/src/roundcubemail; \ 14 | cd /usr/src/roundcubemail; \ 15 | rm -rf installer tests .ci .github .gitignore .editorconfig .tx .travis.yml; \ 16 | [ -f public_html/installer.php ] && rm -f public_html/installer.php; \ 17 | make css-elastic; \ 18 | composer require kolab/net_ldap3 --no-install; \ 19 | composer require bjeavons/zxcvbn-php --no-install; \ 20 | composer install --no-dev --prefer-dist; \ 21 | bin/install-jsdeps.sh; \ 22 | bin/updatecss.sh; \ 23 | rm -rf vendor/masterminds/html5/test \ 24 | vendor/pear/*/tests vendor/*/*/.git* \ 25 | vendor/pear/crypt_gpg/tools \ 26 | vendor/pear/console_commandline/docs \ 27 | vendor/pear/mail_mime/scripts \ 28 | vendor/pear/net_ldap2/doc \ 29 | vendor/pear/net_smtp/docs \ 30 | vendor/pear/net_smtp/examples \ 31 | vendor/pear/net_smtp/README.rst \ 32 | vendor/endroid/qrcode/tests \ 33 | temp/js_cache 34 | 35 | ### Final image 36 | FROM docker.io/roundcube/roundcubemail:latest-apache 37 | 38 | RUN mkdir -p /usr/src 39 | COPY --from=builder /usr/src/roundcubemail /usr/src/roundcubemail 40 | -------------------------------------------------------------------------------- /nightly/README.md: -------------------------------------------------------------------------------- 1 | # Roundcube Dockerfile for Nightly Builds 2 | 3 | The `Dockerfile` in this directory can be used to create nightly builds of Roundcube Webmail from Git master. 4 | It's not recommended to use these builds for productive environments. 5 | 6 | Build from this directory with 7 | 8 | ``` 9 | docker build -t roundcubemail:nightly-`date +%Y%m%d` . 10 | ``` 11 | -------------------------------------------------------------------------------- /templates/Dockerfile-alpine.templ: -------------------------------------------------------------------------------- 1 | FROM php:8.1-%%VARIANT%% as root 2 | LABEL maintainer="Thomas Bruederli " 3 | LABEL org.opencontainers.image.source="https://github.com/roundcube/roundcubemail-docker" 4 | 5 | # This should be done by the upstream images, but as long as they don't do it, 6 | # we rather use our own hands than suffer from outdated packages. 7 | # Kept as standalone command to make it stand out and be easy to remove. 8 | RUN apk upgrade --no-cache 9 | 10 | RUN set -ex; \ 11 | if [ "%%VARIANT%%" = "apache" ]; then a2enmod rewrite; fi; \ 12 | \ 13 | apk add --no-cache \ 14 | bash \ 15 | coreutils \ 16 | rsync \ 17 | tzdata \ 18 | aspell \ 19 | aspell-en \ 20 | unzip 21 | 22 | RUN set -ex; \ 23 | \ 24 | apk add --no-cache --virtual .build-deps \ 25 | $PHPIZE_DEPS \ 26 | icu-dev \ 27 | freetype-dev \ 28 | imagemagick-dev \ 29 | libjpeg-turbo-dev \ 30 | libpng-dev \ 31 | libzip-dev \ 32 | libtool \ 33 | openldap-dev \ 34 | postgresql-dev \ 35 | sqlite-dev \ 36 | aspell-dev \ 37 | ; \ 38 | \ 39 | # Extract sources to avoid using pecl (https://github.com/docker-library/php/issues/374#issuecomment-690698974) 40 | pecl bundle -d /usr/src/php/ext imagick; \ 41 | pecl bundle -d /usr/src/php/ext redis; \ 42 | docker-php-ext-configure gd --with-jpeg --with-freetype; \ 43 | docker-php-ext-configure ldap; \ 44 | docker-php-ext-install \ 45 | exif \ 46 | gd \ 47 | intl \ 48 | ldap \ 49 | pdo_mysql \ 50 | pdo_pgsql \ 51 | pdo_sqlite \ 52 | zip \ 53 | pspell \ 54 | imagick \ 55 | redis \ 56 | ; \ 57 | docker-php-ext-enable imagick opcache redis; \ 58 | docker-php-source delete; \ 59 | rm -r /tmp/pear; \ 60 | # Display installed modules 61 | php -m; \ 62 | \ 63 | extdir="$(php -r 'echo ini_get("extension_dir");')"; \ 64 | runDeps="$( \ 65 | scanelf --needed --nobanner --format '%n#p' --recursive $extdir \ 66 | | tr ',' '\n' \ 67 | | sort -u \ 68 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 69 | )"; \ 70 | apk add --virtual .roundcubemail-phpext-rundeps imagemagick $runDeps; \ 71 | apk del .build-deps; \ 72 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 73 | [ -z "$err" ] || (echo "Sanity check failed: php returned errors; $err"; exit 1;); \ 74 | # include the wait-for-it.sh script (latest commit) 75 | curl -fL https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh -o /wait-for-it.sh; \ 76 | chmod +x /wait-for-it.sh; 77 | 78 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 79 | 80 | # Use the default production configuration 81 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 82 | 83 | # use custom PHP settings 84 | COPY php.ini /usr/local/etc/php/conf.d/roundcube-defaults.ini 85 | 86 | COPY --chmod=0755 docker-entrypoint.sh / 87 | 88 | # Define Roundcubemail version 89 | ENV ROUNDCUBEMAIL_VERSION %%VERSION%% 90 | 91 | # Define the GPG key used for the bundle verification process 92 | ENV ROUNDCUBEMAIL_KEYID "F3E4 C04B B3DB 5D42 15C4 5F7F 5AB2 BAA1 41C4 F7D5" 93 | 94 | # Download package and extract to web volume 95 | RUN set -ex; \ 96 | apk add --no-cache gnupg; \ 97 | curl -o roundcubemail.tar.gz -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz; \ 98 | curl -o roundcubemail.tar.gz.asc -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz.asc; \ 99 | export GNUPGHOME="$(mktemp -d)"; \ 100 | curl -fSL https://roundcube.net/download/pubkey.asc -o /tmp/pubkey.asc; \ 101 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o 'Key fingerprint') != 1 ]; then echo 'The key file should contain only one GPG key'; exit 1; fi; \ 102 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o "${ROUNDCUBEMAIL_KEYID}") != 1 ]; then echo 'The key ID should be the roundcube one'; exit 1; fi; \ 103 | gpg --batch --import /tmp/pubkey.asc; \ 104 | rm /tmp/pubkey.asc; \ 105 | gpg --batch --verify roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 106 | gpgconf --kill all; \ 107 | mkdir /usr/src/roundcubemail; \ 108 | tar -xf roundcubemail.tar.gz -C /usr/src/roundcubemail --strip-components=1 --no-same-owner; \ 109 | rm -r "$GNUPGHOME" roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 110 | rm -rf /usr/src/roundcubemail/installer; \ 111 | chown -R www-data:www-data /usr/src/roundcubemail/logs; \ 112 | # Create the config dir 113 | mkdir -p /var/roundcube/config /var/roundcube/enigma; \ 114 | chown -R www-data:www-data /var/roundcube; \ 115 | chmod +t /var/roundcube 116 | 117 | ENTRYPOINT ["/docker-entrypoint.sh"] 118 | CMD ["%%CMD%%"] 119 | 120 | 121 | #### non-root stage 122 | 123 | FROM root as nonroot 124 | 125 | USER 82:82 -------------------------------------------------------------------------------- /templates/Dockerfile-debian.templ: -------------------------------------------------------------------------------- 1 | FROM php:8.1-%%VARIANT%% as root 2 | LABEL maintainer="Thomas Bruederli " 3 | LABEL org.opencontainers.image.source="https://github.com/roundcube/roundcubemail-docker" 4 | 5 | # This should be done by the upstream images, but as long as they don't do it, 6 | # we rather use our own hands than suffer from outdated packages. 7 | # Kept as standalone command to make it stand out and be easy to remove. 8 | RUN apt-get update && apt-get -y upgrade && apt-get clean 9 | 10 | RUN set -ex; \ 11 | if [ "%%VARIANT%%" = "apache" ]; then \ 12 | a2enmod rewrite; \ 13 | # Make Apache use public_html/ as document root to protect files outside of it \ 14 | # against unauthorized access. \ 15 | # This is possible and recommended since a while, and will be required for Roundcubemail v1.7. \ 16 | sed -i -e 's|\(DocumentRoot /var/www/html\)$|\1/public_html|' /etc/apache2/sites-available/000-default.conf; \ 17 | fi; \ 18 | apt-get update; \ 19 | \ 20 | savedAptMark="$(apt-mark showmanual)"; \ 21 | \ 22 | apt-get install -y --no-install-recommends \ 23 | libfreetype6-dev \ 24 | libicu-dev \ 25 | libjpeg62-turbo-dev \ 26 | libldap2-dev \ 27 | libmagickwand-dev \ 28 | libpng-dev \ 29 | libpq-dev \ 30 | libsqlite3-dev \ 31 | libzip-dev \ 32 | libpspell-dev \ 33 | libonig-dev \ 34 | libldap-common \ 35 | ; \ 36 | # installto.sh & web install dependencies 37 | fetchDeps="gnupg locales libc-l10n"; \ 38 | installDeps="aspell aspell-en rsync unzip"; \ 39 | apt-get install -y --no-install-recommends \ 40 | $installDeps \ 41 | $fetchDeps \ 42 | ; \ 43 | \ 44 | # Extract sources to avoid using pecl (https://github.com/docker-library/php/issues/374#issuecomment-690698974) 45 | pecl bundle -d /usr/src/php/ext imagick; \ 46 | pecl bundle -d /usr/src/php/ext redis; \ 47 | debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ 48 | docker-php-ext-configure gd --with-jpeg --with-freetype; \ 49 | docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ 50 | docker-php-ext-install \ 51 | exif \ 52 | gd \ 53 | intl \ 54 | ldap \ 55 | pdo_mysql \ 56 | pdo_pgsql \ 57 | pdo_sqlite \ 58 | zip \ 59 | pspell \ 60 | imagick \ 61 | redis \ 62 | ; \ 63 | docker-php-ext-enable imagick opcache redis; \ 64 | docker-php-source delete; \ 65 | rm -r /tmp/pear; \ 66 | # Display installed modules 67 | php -m; \ 68 | \ 69 | # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies 70 | apt-mark auto '.*' > /dev/null; \ 71 | apt-mark manual $savedAptMark $installDeps $fetchDeps; \ 72 | extdir="$(php -r 'echo ini_get("extension_dir");')"; \ 73 | ldd "$extdir"/*.so \ 74 | | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ 75 | | sort -u \ 76 | | xargs -r dpkg-query -S \ 77 | | cut -d: -f1 \ 78 | | sort -u \ 79 | | xargs -rt apt-mark manual; \ 80 | \ 81 | apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ 82 | rm -rf /var/lib/apt/lists/*; \ 83 | ldd "$extdir"/*.so | grep -qzv "=> not found" || (echo "Sanity check failed: missing libraries:"; ldd "$extdir"/*.so | grep " => not found"; exit 1); \ 84 | ldd "$extdir"/*.so | grep -q "libzip.so.* => .*/libzip.so.*" || (echo "Sanity check failed: libzip.so is not referenced"; ldd "$extdir"/*.so; exit 1); \ 85 | err="$(php --version 3>&1 1>&2 2>&3)"; \ 86 | [ -z "$err" ] || (echo "Sanity check failed: php returned errors; $err"; exit 1;); \ 87 | # include the wait-for-it.sh script (latest commit) 88 | curl -fL https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh -o /wait-for-it.sh; \ 89 | chmod +x /wait-for-it.sh; 90 | 91 | COPY --from=composer:2 /usr/bin/composer /usr/bin/composer 92 | 93 | # Use the default production configuration 94 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 95 | 96 | # use custom PHP settings 97 | COPY php.ini /usr/local/etc/php/conf.d/roundcube-defaults.ini 98 | 99 | COPY --chmod=0755 docker-entrypoint.sh / 100 | 101 | # Define Roundcubemail version 102 | ENV ROUNDCUBEMAIL_VERSION %%VERSION%% 103 | 104 | # Define the GPG key used for the bundle verification process 105 | ENV ROUNDCUBEMAIL_KEYID "F3E4 C04B B3DB 5D42 15C4 5F7F 5AB2 BAA1 41C4 F7D5" 106 | 107 | # Download package and extract to web volume 108 | RUN set -ex; \ 109 | curl -o roundcubemail.tar.gz -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz; \ 110 | curl -o roundcubemail.tar.gz.asc -fSL https://github.com/roundcube/roundcubemail/releases/download/${ROUNDCUBEMAIL_VERSION}/roundcubemail-${ROUNDCUBEMAIL_VERSION}-complete.tar.gz.asc; \ 111 | export GNUPGHOME="$(mktemp -d)"; \ 112 | curl -fSL https://roundcube.net/download/pubkey.asc -o /tmp/pubkey.asc; \ 113 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o 'Key fingerprint') != 1 ]; then echo 'The key file should contain only one GPG key'; exit 1; fi; \ 114 | LC_ALL=C.UTF-8 gpg -n --show-keys --with-fingerprint --keyid-format=long /tmp/pubkey.asc | if [ $(grep -c -o "${ROUNDCUBEMAIL_KEYID}") != 1 ]; then echo 'The key ID should be the roundcube one'; exit 1; fi; \ 115 | gpg --batch --import /tmp/pubkey.asc; \ 116 | rm /tmp/pubkey.asc; \ 117 | gpg --batch --verify roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 118 | gpgconf --kill all; \ 119 | mkdir /usr/src/roundcubemail; \ 120 | tar -xf roundcubemail.tar.gz -C /usr/src/roundcubemail --strip-components=1 --no-same-owner; \ 121 | rm -r "$GNUPGHOME" roundcubemail.tar.gz.asc roundcubemail.tar.gz; \ 122 | rm -rf /usr/src/roundcubemail/installer; \ 123 | chown -R www-data:www-data /usr/src/roundcubemail/logs; \ 124 | # Create the config dir 125 | mkdir -p /var/roundcube/config /var/roundcube/enigma; \ 126 | chown -R www-data:www-data /var/roundcube; \ 127 | chmod +t /var/roundcube 128 | 129 | ENTRYPOINT ["/docker-entrypoint.sh"] 130 | CMD ["%%CMD%%"] 131 | 132 | 133 | #### non-root stage 134 | 135 | FROM root as nonroot 136 | 137 | # Prepare locale config for locale-gen 138 | RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen; \ 139 | /usr/sbin/locale-gen 140 | 141 | %%NONROOT_ADD%% 142 | 143 | USER 33:33 -------------------------------------------------------------------------------- /templates/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -ex 3 | 4 | # PWD=`pwd` 5 | 6 | if [[ "$1" == apache2* || "$1" == php-fpm || "$1" == bin* ]]; then 7 | INSTALLDIR=`pwd` 8 | # docroot is empty 9 | if ! [ -e index.php -a -e bin/installto.sh ]; then 10 | echo >&2 "roundcubemail not found in $PWD - copying now..." 11 | if [ "$(ls -A)" ]; then 12 | echo >&2 "WARNING: $PWD is not empty - press Ctrl+C now if this is an error!" 13 | ( set -x; ls -A; sleep 10 ) 14 | fi 15 | tar cf - --one-file-system -C /usr/src/roundcubemail . | tar xf - 16 | echo >&2 "Complete! ROUNDCUBEMAIL has been successfully copied to $INSTALLDIR" 17 | # update Roundcube in docroot 18 | else 19 | echo >&2 "roundcubemail found in $INSTALLDIR - installing update..." 20 | (cd /usr/src/roundcubemail && bin/installto.sh -y $INSTALLDIR) 21 | # Re-install composer modules (including plugins) 22 | composer \ 23 | --working-dir=${INSTALLDIR} \ 24 | --prefer-dist \ 25 | --no-dev \ 26 | --no-interaction \ 27 | --optimize-autoloader \ 28 | install 29 | fi 30 | 31 | if [ -f /run/secrets/roundcube_db_user ]; then 32 | ROUNDCUBEMAIL_DB_USER=`cat /run/secrets/roundcube_db_user` 33 | fi 34 | if [ -f /run/secrets/roundcube_db_password ]; then 35 | ROUNDCUBEMAIL_DB_PASSWORD=`cat /run/secrets/roundcube_db_password` 36 | fi 37 | if [ -f /run/secrets/roundcube_oauth_client_secret ]; then 38 | ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET=`cat /run/secrets/roundcube_oauth_client_secret` 39 | fi 40 | 41 | if [ ! -z "${!POSTGRES_ENV_POSTGRES_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "pgsql" ]; then 42 | : "${ROUNDCUBEMAIL_DB_TYPE:=pgsql}" 43 | : "${ROUNDCUBEMAIL_DB_HOST:=postgres}" 44 | : "${ROUNDCUBEMAIL_DB_PORT:=5432}" 45 | : "${ROUNDCUBEMAIL_DB_USER:=${POSTGRES_ENV_POSTGRES_USER}}" 46 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${POSTGRES_ENV_POSTGRES_PASSWORD}}" 47 | : "${ROUNDCUBEMAIL_DB_NAME:=${POSTGRES_ENV_POSTGRES_DB:-roundcubemail}}" 48 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 49 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 50 | # DO NOT USE wait-for-it.sh 51 | else 52 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 53 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 54 | fi 55 | elif [ ! -z "${!MYSQL_ENV_MYSQL_*}" ] || [ "$ROUNDCUBEMAIL_DB_TYPE" == "mysql" ]; then 56 | : "${ROUNDCUBEMAIL_DB_TYPE:=mysql}" 57 | : "${ROUNDCUBEMAIL_DB_HOST:=mysql}" 58 | : "${ROUNDCUBEMAIL_DB_PORT:=3306}" 59 | : "${ROUNDCUBEMAIL_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" 60 | if [ "$ROUNDCUBEMAIL_DB_USER" = 'root' ]; then 61 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD}}" 62 | else 63 | : "${ROUNDCUBEMAIL_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD}}" 64 | fi 65 | : "${ROUNDCUBEMAIL_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-roundcubemail}}" 66 | if [[ "$ROUNDCUBEMAIL_DB_HOST" == unix* ]]; then 67 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}/${ROUNDCUBEMAIL_DB_NAME}}" 68 | # DO NOT USE wait-for-it.sh 69 | else 70 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}://${ROUNDCUBEMAIL_DB_USER}:${ROUNDCUBEMAIL_DB_PASSWORD}@${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT}/${ROUNDCUBEMAIL_DB_NAME}}" 71 | /wait-for-it.sh ${ROUNDCUBEMAIL_DB_HOST}:${ROUNDCUBEMAIL_DB_PORT} -t 30 72 | fi 73 | else 74 | # use local SQLite DB in /var/roundcube/db 75 | : "${ROUNDCUBEMAIL_DB_TYPE:=sqlite}" 76 | : "${ROUNDCUBEMAIL_DB_DIR:=/var/roundcube/db}" 77 | : "${ROUNDCUBEMAIL_DB_NAME:=sqlite}" 78 | : "${ROUNDCUBEMAIL_DSNW:=${ROUNDCUBEMAIL_DB_TYPE}:///$ROUNDCUBEMAIL_DB_DIR/${ROUNDCUBEMAIL_DB_NAME}.db?mode=0646}" 79 | 80 | mkdir -p $ROUNDCUBEMAIL_DB_DIR 81 | chown www-data:www-data $ROUNDCUBEMAIL_DB_DIR 82 | fi 83 | 84 | : "${ROUNDCUBEMAIL_DEFAULT_HOST:=localhost}" 85 | : "${ROUNDCUBEMAIL_DEFAULT_PORT:=143}" 86 | : "${ROUNDCUBEMAIL_SMTP_SERVER:=localhost}" 87 | : "${ROUNDCUBEMAIL_SMTP_PORT:=587}" 88 | : "${ROUNDCUBEMAIL_PLUGINS:=archive,zipdownload}" 89 | : "${ROUNDCUBEMAIL_SKIN:=elastic}" 90 | : "${ROUNDCUBEMAIL_TEMP_DIR:=/tmp/roundcube-temp}" 91 | : "${ROUNDCUBEMAIL_REQUEST_PATH:=/}" 92 | : "${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER:=$INSTALLDIR}" 93 | 94 | if [ ! -z "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" ]; then 95 | echo "Installing plugins from the list" 96 | echo "Plugins: ${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" 97 | 98 | # Change ',' into a space 99 | ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH=`echo "${ROUNDCUBEMAIL_COMPOSER_PLUGINS}" | tr ',' ' '` 100 | 101 | composer \ 102 | --working-dir=${ROUNDCUBEMAIL_COMPOSER_PLUGINS_FOLDER} \ 103 | --prefer-dist \ 104 | --prefer-stable \ 105 | --update-no-dev \ 106 | --no-interaction \ 107 | --optimize-autoloader \ 108 | require \ 109 | -- \ 110 | ${ROUNDCUBEMAIL_COMPOSER_PLUGINS_SH}; 111 | fi 112 | 113 | if [ ! -d skins/${ROUNDCUBEMAIL_SKIN} ]; then 114 | # Installing missing skin 115 | echo "Installing missing skin: ${ROUNDCUBEMAIL_SKIN}" 116 | composer \ 117 | --working-dir=${INSTALLDIR} \ 118 | --prefer-dist \ 119 | --prefer-stable \ 120 | --update-no-dev \ 121 | --no-interaction \ 122 | --optimize-autoloader \ 123 | require \ 124 | -- \ 125 | roundcube/${ROUNDCUBEMAIL_SKIN}; 126 | fi 127 | 128 | if [ ! -e config/config.inc.php ]; then 129 | GENERATED_DES_KEY=`head /dev/urandom | base64 | head -c 24` 130 | touch config/config.inc.php 131 | 132 | echo "Write root config to $PWD/config/config.inc.php" 133 | echo " config/config.inc.php 142 | 143 | elif ! grep -q "config.docker.inc.php" config/config.inc.php; then 144 | echo "include(__DIR__ . '/config.docker.inc.php');" >> config/config.inc.php 145 | fi 146 | 147 | ROUNDCUBEMAIL_PLUGINS_PHP=`echo "${ROUNDCUBEMAIL_PLUGINS}" | sed -E "s/[, ]+/', '/g"` 148 | echo "Write Docker config to $PWD/config/config.docker.inc.php" 149 | echo " config/config.docker.inc.php 160 | 161 | if [ -e /run/secrets/roundcube_des_key ]; then 162 | echo "\$config['des_key'] = file_get_contents('/run/secrets/roundcube_des_key');" >> config/config.docker.inc.php 163 | elif [ ! -z "${ROUNDCUBEMAIL_DES_KEY}" ]; then 164 | echo "\$config['des_key'] = getenv('ROUNDCUBEMAIL_DES_KEY');" >> config/config.docker.inc.php 165 | fi 166 | 167 | if [ ! -z "${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}" ]; then 168 | echo "\$config['oauth_client_secret'] = '${ROUNDCUBEMAIL_OAUTH_CLIENT_SECRET}';" >> config/config.docker.inc.php 169 | fi 170 | 171 | if [ ! -z "${ROUNDCUBEMAIL_SPELLCHECK_URI}" ]; then 172 | echo "\$config['spellcheck_engine'] = 'googie';" >> config/config.docker.inc.php 173 | echo "\$config['spellcheck_uri'] = '${ROUNDCUBEMAIL_SPELLCHECK_URI}';" >> config/config.docker.inc.php 174 | fi 175 | 176 | # If the "enigma" plugin is enabled but has no storage configured, inject a default value for the mandatory setting. 177 | if $(echo $ROUNDCUBEMAIL_PLUGINS | grep -Eq '\benigma\b') && ! grep -qr enigma_pgp_homedir /var/roundcube/config/; then 178 | echo "\$config['enigma_pgp_homedir'] = '/var/roundcube/enigma';" >> config/config.docker.inc.php 179 | fi 180 | 181 | # include custom config files 182 | for fn in `ls /var/roundcube/config/*.php 2>/dev/null || true`; do 183 | echo "include('$fn');" >> config/config.docker.inc.php 184 | done 185 | 186 | # initialize or update DB 187 | bin/initdb.sh --dir=$PWD/SQL --update || echo "Failed to initialize/update the database. Please start with an empty database and restart the container." 188 | 189 | if [ ! -z "${ROUNDCUBEMAIL_TEMP_DIR}" ]; then 190 | mkdir -p ${ROUNDCUBEMAIL_TEMP_DIR} && chown www-data ${ROUNDCUBEMAIL_TEMP_DIR} 191 | fi 192 | 193 | if [ ! -z "${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" ]; then 194 | echo "upload_max_filesize=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 195 | echo "post_max_size=${ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE}" >> /usr/local/etc/php/conf.d/roundcube-override.ini 196 | fi 197 | 198 | : "${ROUNDCUBEMAIL_LOCALE:=en_US.UTF-8 UTF-8}" 199 | 200 | if [ -e /usr/sbin/locale-gen ] && [ ! -f /etc/locale.gen ] && [ ! -z "${ROUNDCUBEMAIL_LOCALE}" ]; then 201 | echo "${ROUNDCUBEMAIL_LOCALE}" > /etc/locale.gen && /usr/sbin/locale-gen 202 | fi 203 | 204 | if [ ! -z "${ROUNDCUBEMAIL_ASPELL_DICTS}" ]; then 205 | ASPELL_PACKAGES=`echo -n "aspell-${ROUNDCUBEMAIL_ASPELL_DICTS}" | sed -E "s/[, ]+/ aspell-/g"` 206 | which apt-get && apt-get update && apt-get install -y $ASPELL_PACKAGES 207 | which apk && apk add --no-cache $ASPELL_PACKAGES 208 | fi 209 | 210 | fi 211 | 212 | exec "$@" 213 | -------------------------------------------------------------------------------- /templates/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=64M 2 | display_errors=Off 3 | log_errors=On 4 | upload_max_filesize=5M 5 | post_max_size=6M 6 | zlib.output_compression=Off 7 | session.auto_start=Off 8 | session.gc_maxlifetime=21600 9 | session.gc_divisor=500 10 | session.gc_probability=1 11 | -------------------------------------------------------------------------------- /tests/docker-compose.test-apache-postgres.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | roundcubemail: 5 | image: ${ROUNDCUBEMAIL_TEST_IMAGE:-roundcube/roundcubemail:latest-apache} 6 | ports: 7 | - 80:${HTTP_PORT:-80} 8 | healthcheck: 9 | # To make it obvious in logs "ping=ping" is added 10 | test: ["CMD", "curl", "--fail", "http://localhost:${HTTP_PORT:-80}/?ping=ping"] 11 | interval: 2s 12 | timeout: 3s 13 | retries: 30 14 | start_period: 10s 15 | depends_on: 16 | roundcubedb: 17 | condition: service_healthy 18 | networks: 19 | roundcube_test_net: 20 | aliases: 21 | - roundcubemail 22 | environment: 23 | - ROUNDCUBEMAIL_DB_TYPE=pgsql 24 | - ROUNDCUBEMAIL_DB_HOST=roundcubedb # same as pgsql container name 25 | - ROUNDCUBEMAIL_DB_NAME=roundcube # same as pgsql POSTGRES_DB env name 26 | - ROUNDCUBEMAIL_DB_USER=roundcube # same as pgsql POSTGRES_USER env name 27 | - ROUNDCUBEMAIL_DB_PASSWORD=roundcube # same as pgsql POSTGRES_PASSWORD env name 28 | - ROUNDCUBEMAIL_SKIN=larry # Install non-default skin 29 | 30 | roundcubedb: 31 | image: postgres:alpine 32 | healthcheck: 33 | # "roundcube" is the POSTGRES_USER value 34 | test: ["CMD-SHELL", "pg_isready -U roundcube"] 35 | interval: 2s 36 | timeout: 3s 37 | retries: 30 38 | start_period: 10s 39 | networks: 40 | roundcube_test_net: 41 | aliases: 42 | - roundcubedb 43 | environment: 44 | - POSTGRES_DB=roundcube 45 | - POSTGRES_USER=roundcube 46 | - POSTGRES_PASSWORD=roundcube 47 | 48 | # A name that matches Docker auto test naming, you want a name here is one 49 | # Source: https://docs.docker.com/docker-hub/builds/automated-testing/#set-up-automated-test-files 50 | sut: 51 | image: alpine:3.14 52 | networks: 53 | roundcube_test_net: 54 | depends_on: 55 | roundcubemail: 56 | condition: service_healthy 57 | roundcubedb: 58 | condition: service_healthy 59 | command: /tests/run.sh 60 | environment: 61 | - ROUNDCUBE_URL=http://roundcubemail:${HTTP_PORT:-80}/ 62 | volumes: 63 | - ./run.sh:/tests/run.sh:ro 64 | working_dir: /tests 65 | networks: 66 | roundcube_test_net: 67 | -------------------------------------------------------------------------------- /tests/docker-compose.test-fpm-postgres.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | roundcubemail-fpm: 5 | image: ${ROUNDCUBEMAIL_TEST_IMAGE:-roundcube/roundcubemail:latest-fpm} 6 | healthcheck: 7 | # Check until the FPM port is in in the LISTEN list 8 | # test: ["CMD-SHELL", "netstat -an | grep -q -F \":9000\""] 9 | # Or use php to test php since the non alpine fpm image has no binary able to know if a port is in listen mode 10 | test: ["CMD-SHELL", "php -r '$$c = @fsockopen(\"localhost\", 9000); if (is_resource($$c)) { fwrite(STDOUT, \"OK\"); fclose($$c); exit(0); } else { fwrite(STDERR, \"FAIL\"); exit(1); }'"] 11 | interval: 2s 12 | timeout: 3s 13 | retries: 30 14 | start_period: 10s 15 | depends_on: 16 | roundcubedb: 17 | condition: service_healthy 18 | networks: 19 | roundcube_test_net: 20 | aliases: 21 | - roundcubemail-fpm 22 | volumes: 23 | - www-vol:/var/www/html 24 | environment: 25 | - ROUNDCUBEMAIL_DB_TYPE=pgsql 26 | - ROUNDCUBEMAIL_DB_HOST=roundcubedb # same as pgsql container name 27 | - ROUNDCUBEMAIL_DB_NAME=roundcube # same as pgsql POSTGRES_DB env name 28 | - ROUNDCUBEMAIL_DB_USER=roundcube # same as pgsql POSTGRES_USER env name 29 | - ROUNDCUBEMAIL_DB_PASSWORD=roundcube # same as pgsql POSTGRES_PASSWORD env name 30 | - ROUNDCUBEMAIL_PLUGINS=enigma 31 | - ROUNDCUBEMAIL_SKIN=larry # Install non-default skin 32 | 33 | roundcubedb: 34 | image: postgres:alpine 35 | healthcheck: 36 | # "roundcube" is the POSTGRES_USER value 37 | test: ["CMD-SHELL", "pg_isready -U roundcube"] 38 | interval: 2s 39 | timeout: 3s 40 | retries: 30 41 | start_period: 10s 42 | networks: 43 | roundcube_test_net: 44 | aliases: 45 | - roundcubedb 46 | environment: 47 | - POSTGRES_DB=roundcube 48 | - POSTGRES_USER=roundcube 49 | - POSTGRES_PASSWORD=roundcube 50 | 51 | roundcubenginx: 52 | image: nginx:alpine 53 | healthcheck: 54 | # To make it obvious in logs "ping=ping" is added 55 | test: ["CMD", "curl", "--fail", "http://localhost/?ping=ping"] 56 | interval: 2s 57 | timeout: 3s 58 | retries: 30 59 | start_period: 10s 60 | networks: 61 | roundcube_test_net: 62 | aliases: 63 | - roundcubenginx 64 | depends_on: 65 | roundcubemail-fpm: 66 | condition: service_healthy 67 | volumes: 68 | - www-vol:/var/www/html 69 | - ./nginx-default.conf:/etc/nginx/conf.d/default.conf 70 | environment: 71 | - NGINX_HOST=localhost # set your local domain or your live domain 72 | 73 | # A name that matches Docker auto test naming, you want a name here is one 74 | # Source: https://docs.docker.com/docker-hub/builds/automated-testing/#set-up-automated-test-files 75 | sut: 76 | image: alpine:3.14 77 | networks: 78 | roundcube_test_net: 79 | depends_on: 80 | roundcubenginx: 81 | condition: service_healthy 82 | roundcubemail-fpm: 83 | condition: service_healthy 84 | roundcubedb: 85 | condition: service_healthy 86 | command: /tests/run.sh 87 | volumes: 88 | - ./run.sh:/tests/run.sh:ro 89 | working_dir: /tests 90 | environment: 91 | ROUNDCUBE_URL: http://roundcubenginx/ 92 | networks: 93 | roundcube_test_net: 94 | 95 | volumes: 96 | www-vol: 97 | -------------------------------------------------------------------------------- /tests/nginx-default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | server_tokens off; 5 | autoindex off; 6 | 7 | root /var/www/html; 8 | 9 | location / { 10 | index index.php; 11 | } 12 | 13 | location ~ \.php$ { 14 | fastcgi_pass roundcubemail-fpm:9000; 15 | # regex to split $uri to $fastcgi_script_name and $fastcgi_path 16 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 17 | 18 | # Check that the PHP script exists before passing it 19 | try_files $fastcgi_script_name =404; 20 | 21 | # Bypass the fact that try_files resets $fastcgi_path_info 22 | # see: https://trac.nginx.org/nginx/ticket/321 23 | set $path_info $fastcgi_path_info; 24 | fastcgi_param PATH_INFO $path_info; 25 | 26 | fastcgi_index index.php; 27 | include fastcgi.conf; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | # findText needle haystack 6 | findText () { 7 | # avoid grep exit code 1 on non match using cat 8 | NBR="$(echo "${2}" | grep -c -F "${1}" | cat)" 9 | if [ $NBR -gt 0 ]; then 10 | echo "[OK] Found \"${1}\" ${NBR} time(s) in the input" > /dev/stdout 11 | return 0 12 | fi 13 | echo "[FAIL] \"${1}\" was not found in the input \"${2}\"" > /dev/stderr 14 | return 1 15 | } 16 | 17 | echo 'Installing pckages' 18 | 19 | apk add --no-cache --update html2text curl 20 | 21 | echo 'Starting tests...' 22 | ROUNDCUBE_URL="${ROUNDCUBE_URL:-"http://roundcubemail/"}" 23 | echo 'Fetching homepage' 24 | HOMEPAGE_TEXT=$(curl -s --fail "${ROUNDCUBE_URL}" | html2text) 25 | echo 'Checking homepage' 26 | findText 'Roundcube' "${HOMEPAGE_TEXT}" 27 | findText 'Roundcube Webmail' "${HOMEPAGE_TEXT}" 28 | findText 'Username [_user ]' "${HOMEPAGE_TEXT}" 29 | findText 'Password [********************]' "${HOMEPAGE_TEXT}" 30 | findText 'Login' "${HOMEPAGE_TEXT}" 31 | findText 'Warning: This webmail service requires Javascript!' "${HOMEPAGE_TEXT}" 32 | echo 'Homepage is okay' 33 | 34 | echo 'End.' -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # set -eu 3 | 4 | declare -A CMD=( 5 | [apache]='apache2-foreground' 6 | [fpm]='php-fpm' 7 | [fpm-alpine]='php-fpm' 8 | ) 9 | 10 | declare -A BASE=( 11 | [apache]='debian' 12 | [fpm]='debian' 13 | [fpm-alpine]='alpine' 14 | ) 15 | 16 | VERSION="${1:-$(curl -fsS https://roundcube.net/VERSION.txt)}" 17 | 18 | #set -x 19 | echo "Generating files for version $VERSION..." 20 | 21 | for variant in apache fpm fpm-alpine; do 22 | dir="$variant" 23 | mkdir -p "$dir" 24 | 25 | template="templates/Dockerfile-${BASE[$variant]}.templ" 26 | cp templates/docker-entrypoint.sh "$dir/docker-entrypoint.sh" 27 | cp templates/php.ini "$dir/php.ini" 28 | sed -E -e ' 29 | s/%%VARIANT%%/'"$variant"'/; 30 | s/%%VERSION%%/'"$VERSION"'/; 31 | s/%%CMD%%/'"${CMD[$variant]}"'/; 32 | ' $template | tr '¬' '\n' > "$dir/Dockerfile" 33 | 34 | if [[ -f "$dir/nonroot-add.txt" ]]; then 35 | sed -i -e '/%%NONROOT_ADD%%/ {' -e 'r '"$dir/nonroot-add.txt" -e 'd' -e '}' $dir/Dockerfile 36 | else 37 | sed -i 's/%%NONROOT_ADD%%//' $dir/Dockerfile 38 | fi 39 | 40 | echo "✓ Wrote $dir/Dockerfile" 41 | done 42 | 43 | # Use perl to avoid problems with BSD vs. GNU sed, which have incompatible 44 | # argument syntax for editing files in-place. 45 | perl -pi -e "s/1\.[0-9]\.[0-9]+-/${VERSION}-/" .github/workflows/build.yml 46 | echo "Updating version in build.yml workflow" 47 | 48 | echo "Done." 49 | --------------------------------------------------------------------------------