{"id":18169747,"url":"https://github.com/suprovsky/docker-nextcloud","last_synced_at":"2025-07-25T09:39:17.399Z","repository":{"id":162045605,"uuid":"630417194","full_name":"suprovsky/docker-nextcloud","owner":"suprovsky","description":"All-in-one Nextcloud Docker image, based on Alpine Linux. Aims at being simple and hardened. ","archived":false,"fork":false,"pushed_at":"2024-12-06T07:12:51.000Z","size":365,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-06T07:31:16.811Z","etag":null,"topics":["alpine","docker","nextcloud","security"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/suprovsky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-20T10:36:22.000Z","updated_at":"2024-12-06T07:12:32.000Z","dependencies_parsed_at":"2023-10-16T04:06:28.340Z","dependency_job_id":"8a7efb69-191e-4a8e-8a05-51d6befb8f09","html_url":"https://github.com/suprovsky/docker-nextcloud","commit_stats":{"total_commits":268,"total_committers":26,"mean_commits":"10.307692307692308","dds":0.6231343283582089,"last_synced_commit":"5641741f0172bb8d56922e743946feeebae8133d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suprovsky%2Fdocker-nextcloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suprovsky%2Fdocker-nextcloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suprovsky%2Fdocker-nextcloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suprovsky%2Fdocker-nextcloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/suprovsky","download_url":"https://codeload.github.com/suprovsky/docker-nextcloud/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230640271,"owners_count":18257773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpine","docker","nextcloud","security"],"created_at":"2024-11-02T14:05:32.691Z","updated_at":"2025-07-25T09:39:17.386Z","avatar_url":"https://github.com/suprovsky.png","language":"Dockerfile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# suprovsky/nextcloud\n\n## About\n\nThis non-official image is intended as an **all-in-one** (as in monolithic) Nextcloud **production** image based on [wonderfall/docker-nextcloud](https://github.com/wonderfall/docker-nextcloud) with an inclusion of imagick plugin.\n___\n\n- [suprovsky/nextcloud](#suprovskynextcloud)\n  - [About](#about)\n  - [Features](#features)\n  - [Security](#security)\n  - [Tags](#tags)\n  - [Build-time variables](#build-time-variables)\n  - [Environment variables](#environment-variables)\n    - [Runtime](#runtime)\n    - [Startup](#startup)\n  - [Volumes](#volumes)\n  - [Ports](#ports)\n  - [Migration](#migration)\n  - [Usage](#usage)\n\n## Features\n\n- Based on [Alpine Linux](https://alpinelinux.org/).\n- Fetching PHP/nginx from their official images.\n- **Rootless**: no privilege at any time, even at startup.\n- Uses [s6](https://skarnet.org/software/s6/) as a lightweight process supervisor.\n- Supports MySQL/MariaDB, PostgresQL and SQLite3 database backends.\n- Includes OPcache and APCu for improved caching \u0026 performance, also supports redis.\n- Tarball integrity \u0026 authenticity checked during build process.\n- Includes **hardened_malloc**, [a hardened memory allocator](https://github.com/GrapheneOS/hardened_malloc).\n- Includes **Snuffleupagus**, [a PHP security module](https://github.com/jvoisin/snuffleupagus).\n- Includes a simple **built-in cron** system.\n- Much easier to maintain thanks to multi-stages build.\n- Includes imagick by default.\n\nYou're free to make your own image based on this one if you want a specific feature. Uncommon features won't be included as they can increase attack surface: this image intends to stay **minimal**, but **functional enough** to cover basic needs.\n\n## Security\n\nDon't run random images from random dudes on the Internet. Ideally, you want to maintain and build it yourself.\n\n- **Images are scanned every day** by [Trivy](https://github.com/aquasecurity/trivy) for OS vulnerabilities. Known vulnerabilities will be automatically uploaded to [GitHub Security Lab](https://github.com/suprovsky/docker-nextcloud/security/code-scanning) for full transparency. This also warns me if I have to take action to fix a vulnerability.\n- **Latest tag/version is automatically built weekly**, so you should often update your images regardless if you're already using the latest Nextcloud version.\n- **Build production images without cache** (use `docker build --no-cache` for instance) if you want to build your images manually. Latest dependencies will hence be used instead of outdated ones due to a cached layer.\n- **A security module for PHP called [Snuffleupagus](https://github.com/jvoisin/snuffleupagus) is used by default**. This module aims at killing entire bug and security exploit classes (including XXE, weak PRNG, file-upload based code execution), thus raising the cost of attacks. For now we're using a configuration file derived from [the default one](https://github.com/jvoisin/snuffleupagus/blob/master/config/default_php8.rules), with some explicit exceptions related to Nextcloud. This configuration file is tested and shouldn't break basic functionality, but it can cause issues in specific and untested use cases: if that happens to you, get logs from either `syslog` or `/nginx/logs/error.log` inside the container, and [open an issue](https://github.com/suprovsky/docker-nextcloud/issues). You can also disable the security module altogether by changing the `PHP_HARDENING` environment variable to `false` before recreating the container.\n- **Images are signed with the GitHub-provided OIDC token in Actions** using the experimental \"keyless\" signing feature provided by [cosign](https://github.com/sigstore/cosign). You can verify the image signature using `cosign` as well:\n\n```env\nCOSIGN_EXPERIMENTAL=true cosign verify ghcr.io/suprovsky/nextcloud\n```\n\nVerifying the signature isn't a requirement, and might not be as seamless as using *Docker Content Trust* (which is not supported by GitHub's OCI registry). However, it's strongly recommended to do so in a sensitive environment to ensure the authenticity of the images and further limit the risk of supply chain attacks.\n\n## Tags\n\n- `latest` : latest Nextcloud version\n- `x` : latest Nextcloud x.x (e.g. `31`)\n- `x.x.x` : Nextcloud x.x.x (e.g. `31.0.6`)\n\nYou can always have a glance [here](https://github.com/users/suprovsky/packages/container/package/nextcloud).\nOnly the **latest stable version** will be maintained by myself.\n\n*Note: automated builds only target `linux/amd64` (x86_64). There is no technical reason preventing the image to be built for `arm64` (in fact you can build it yourself), but GitHub Actions runners are limited in memory, and this limit makes it currently impossible to target both platforms.*\n\n## Build-time variables\n\n|          Variable           |               Description              |       Default      |\n| --------------------------- | -------------------------------------- | ------------------ |\n| **NEXTCLOUD_VERSION**       | version of Nextcloud                   |          *         |\n| **ALPINE_VERSION**          | version of Alpine Linux                |          *         |\n| **PHP_VERSION**             | version of PHP                         |          *         |\n| **NGINX_VERSION**           | version of nginx                       |          *         |\n| **HARDENED_MALLOC_VERSION** | version of hardened_malloc             |          *         |\n| **SNUFFLEUPAGUS_VERSION**   | version of Snuffleupagus (php ext)     |          *         |\n| **SHA256_SUM**              | checksum of Nextcloud tarball (sha256) |          *         |\n| **GPG_FINGERPRINT**         | fingerprint of Nextcloud GPG key       |          *         |\n| **UID**                     | user id                                |        1000        |\n| **GID**                     | group id                               |        1000        |\n| **CONFIG_NATIVE**           | native code for hardened_malloc        |        false       |\n| **VARIANT**                 | variant of hardened_malloc (see repo)  |        light       |\n\n(latest known available, likely to change regularly)\n\nFor convenience they were put at [the very top of the Dockerfile](https://github.com/suprovsky/docker-nextcloud/blob/main/Dockerfile#L1-L13) and their usage should be quite explicit if you intend to build this image yourself. If you intend to change `NEXTCLOUD_VERSION`, change `SHA256_SUM` accordingly.\n\n## Environment variables\n\n### Runtime\n\n|          Variable         |         Description         |       Default      |\n| ------------------------- | --------------------------- | ------------------ |\n|     **UPLOAD_MAX_SIZE**   | file upload maximum size    |         10G        |\n|      **APC_SHM_SIZE**     | apc shared memory size      |         128M       |\n|    **OPCACHE_MEM_SIZE**   | opcache available memory    |         128M       |\n|      **MEMORY_LIMIT**     | max php command mem usage   |         512M       |\n|       **CRON_PERIOD**     | cron time interval (min.)   |         5m         |\n|   **CRON_MEMORY_LIMIT**   | cron max memory usage       |         1G         |\n|         **DB_TYPE**       | sqlite3, mysql, pgsql       |       sqlite3      |\n|         **DOMAIN**        | host domain                 |      localhost     |\n|      **PHP_HARDENING**    | enables snuffleupagus       |        true        |\n\nLeave them at default if you're not sure what you're doing.\n\n### Startup\n\n|          Variable         |         Description         |\n| ------------------------- | --------------------------- |\n|        **ADMIN_USER**     | admin username              |\n|      **ADMIN_PASSWORD**   | admin password              |\n|         **DB_TYPE**       | sqlite3, mysql, pgsql       |\n|         **DB_NAME**       | name of the database        |\n|         **DB_USER**       | name of the database user   |\n|       **DB_PASSWORD**     | password of the db user     |\n|         **DB_HOST**       | database host               |\n\n`ADMIN_USER` and `ADMIN_PASSWORD` are optional and mainly for niche purposes. Obviously, avoid clear text passwords. Once `setup.sh` has run for the first time, these variables can be removed. You should then edit `/nextcloud/config/config.php` directly if you want to change something in your configuration.\n\nThe usage of [Docker secrets](https://docs.docker.com/engine/swarm/secrets/) will be considered in the future, but `config.php` already covers quite a lot.\n\n## Volumes\n\n|          Variable            |         Description        |\n| -------------------------    | -------------------------- |\n| **/data**                    |         data files         |\n| **/nextcloud/config**        |        config files        |\n| **/nextcloud/apps2**         |       3rd-party apps       |\n| **/nextcloud/themes**        |        custom themes       |\n| **/php/session**             |      PHP session files     |\n\n*Note: mounting `/php/session` isn't required but could be desirable in some circumstances.*\n\n## Ports\n\n|              Port            |            Use             |\n| -------------------------    | -------------------------- |\n| **8888** (tcp)               |       Nextcloud web        |\n\nA reverse proxy like [Traefik](https://doc.traefik.io/traefik/) or [Caddy](https://caddyserver.com/) can be used, and you should consider:\n\n- Redirecting all HTTP traffic to HTTPS\n- Setting the [HSTS header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) correctly\n\n## Migration\n\nFrom now on you'll need to make sure all volumes have proper permissions. The default UID/GID is now 1000, so you'll need to build the image yourself if you want to change that, or you can just change the actual permissions of the volumes using `chown -R 1000:1000`. The flexibility provided by the legacy image came at some cost (performance \u0026 security), therefore this feature won't be provided anymore.\n\nOther changes that should be reflected in your configuration files:\n\n- `/config` volume is now `/nextcloud/config`\n- `/apps2` volume is now `/nextcloud/apps2`\n- `ghcr.io/suprovsky/nextcloud` is the new image location\n\nYou should edit your `docker-compose.yml` and `config.php` accordingly.\n\n## Usage\n\nExample `docker-compose.yml`:\n\n```docker\nservices:\n  nextcloud:\n    depends_on:\n      nextcloud-db:\n        condition: service_healthy\n      redis:\n        condition: service_healthy\n    image: ghcr.io/suprovsky/nextcloud:latest\n    restart: always\n    ports:\n      - '8888:8888'\n    volumes:\n      - 'nextcloud-themes:/nextcloud/themes'\n      - 'nextcloud-apps:/nextcloud/apps2'\n      - 'nextcloud-config:/nextcloud/config'\n      - 'nextcloud-data:/data'\n      - './logs:/logs'\n    environment:\n      - UPLOAD_MAX_SIZE=${UPLOAD_MAX_SIZE}\n      - APC_SHM_SIZE=${APC_SHM_SIZE}\n      - MEMORY_LIMIT=${MEMORY_LIMIT}\n      - CRON_PERIOD=${CRON_PERIOD}\n      - CRON_MEMORY_LIMIT=${CRON_MEMORY_LIMIT}\n      - DB_NAME=${DB_NAME}\n      - DB_TYPE=${DB_TYPE}\n      - DB_USER=${DB_USER}\n      - DB_PASSWORD=${DB_PASSWORD}\n      - DOMAIN=${DOMAIN}\n      - PHP_HARDENING=${PHP_HARDENING}\n    env_file: './.env'\n    healthcheck:\n      test: curl -sSf 'http://localhost:8888/status.php' | grep '\"installed\":true' | grep '\"maintenance\":false' | grep '\"needsDbUpgrade\":false' || exit 1\n      interval: 10s\n      timeout: 5s\n      retries: 10\n      #command: sleep infinity\n  db:\n    image: bitnami/mariadb:10.11\n    restart: always\n    volumes:\n      - 'nextcloud-db:/bitnami/mariadb'\n      - './custom.cnf:/opt/bitnami/mariadb/conf/my_custom.cnf:ro'\n    environment:\n      - MARIADB_DATABASE=${DB_NAME}\n      - MARIADB_PASSWORD=${DB_PASSWORD}\n      - MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}\n      - MARIADB_USER=${DB_USER}\n    env_file: './.env'\n    healthcheck:\n      test: ['CMD', '/opt/bitnami/scripts/mariadb/healthcheck.sh']\n      interval: 15s\n      timeout: 5s\n      retries: 6\n  redis:\n    image: bitnami/redis:7.4\n    restart: always\n    environment:\n      - REDISCLI_AUTH=${REDIS_PASSWORD}\n    env_file:\n      - ./.env\n    networks:\n      - nextcloud-net\n    volumes:\n      - nextcloud-redis:/bitnami/redis/data:rw\n      - /etc/localtime:/etc/localtime:ro\n      - ./redis.conf:/opt/bitnami/redis/etc/redis.conf\n    healthcheck:\n      test: redis-cli ping | grep PONG\n      interval: 10s\n      timeout: 2s\n      retries: 10\n\nvolumes:\n  nextcloud-themes:\n  nextcloud-apps:\n  nextcloud-config:\n  nextcloud-data:\n  nextcloud-db:\n  nextcloud-redis:\n```\n\nExample `.env` file:\n\n```env\nADMIN_USER=supersecretadminuser\nADMIN_PASSWORD=DXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD\nDB_TYPE=mysql\nDB_NAME=nextcloud\nDB_USER=nextclouduser\nDB_PASSWORD=DXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD\nDB_HOST=nextcloud-db\nUPLOAD_MAX_SIZE=50G\nAPC_SHM_SIZE=2G\nMEMORY_LIMIT=2G\nCRON_PERIOD=5m\nCRON_MEMORY_LIMIT=1G\nDOMAIN=nextcloud.example.com\nMARIADB_ROOT_PASSWORD=DXDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD\nPHP_HARDENING=true\nREDIS_PASSWORD=XDXDXDXDXDXDXDXDXDXDXDXDXDXDXDXD\n```\n\nYou must use some reverse proxy in front of the container. I like [Nginx](https://www.nginx.com/), so you can find an example config making use of [Let's Encrypt](https://letsencrypt.org/) certificates below.  \nWith this setup I got 120/100 on [Mozilla Observatory](https://observatory.mozilla.org/), A+ on [Nextcloud Security Scan](https://scan.nextcloud.com/) and A+ on [SSL Labs](https://www.ssllabs.com/).\n\n```nginx\nserver {\n  listen [::]:443 ssl; # managed by Certbot\n  listen 443 ssl; # managed by Certbot\n  ssl_certificate /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/nextcloud.example.com/privkey.pem; # managed by Certbot\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n  server_name nextcloud.example.com;\n  access_log /var/log/nginx/nextcloud.example.com-access.log;\n  error_log /var/log/nginx/nextcloud.example.com-error.log;\n  ssl_session_cache shared:ssl_session_cache:10m;\n  location / {\n    proxy_pass http://127.0.0.1:8888$request_uri;\n    proxy_hide_header X-Content-Type-Options;\n    proxy_hide_header X-XSS-Protection;\n    proxy_hide_header X-Robots-Tag;\n    add_header X-Content-Type-Options \"nosniff\" always;\n    add_header X-Robots-Tag \"noindex, nofollow\" always;\n    add_header X-XSS-Protection \"1; mode=block\" always;\n    add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\";\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header X-Forwarded-Port $server_port;\n    proxy_set_header X-Forwarded-Scheme $scheme;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header Accept-Encoding \"\";\n    proxy_set_header Host $host;\n    proxy_buffering off;\n    client_body_buffer_size 512k;\n    proxy_read_timeout 86400s;\n    client_max_body_size 0;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n  }\n  location /.well-known/carddav {\n    return 301 $scheme://$host/remote.php/dav;\n  }\n  location /.well-known/caldav {\n    return 301 $scheme://$host/remote.php/dav;\n  }\n}\nserver {\n    listen 80;\n    listen [::]:80;\n    server_name nextcloud.example.com;\n    return 302 https://nextcloud.example.com$request_uri;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuprovsky%2Fdocker-nextcloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuprovsky%2Fdocker-nextcloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuprovsky%2Fdocker-nextcloud/lists"}