{"id":15437923,"url":"https://github.com/tchapi/own-private-cloud","last_synced_at":"2025-08-25T00:40:47.736Z","repository":{"id":44255338,"uuid":"212336993","full_name":"tchapi/own-private-cloud","owner":"tchapi","description":"🐳 ⛅ Your own private cloud services with Docker","archived":false,"fork":false,"pushed_at":"2024-11-16T15:51:55.000Z","size":405,"stargazers_count":37,"open_issues_count":0,"forks_count":10,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-12-10T04:10:27.122Z","etag":null,"topics":["cloud","containers","docker","open-source"],"latest_commit_sha":null,"homepage":"https://www.foobarflies.io/your-own-public-cloud-why-not/","language":"HTML","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/tchapi.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":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-02T12:33:41.000Z","updated_at":"2024-11-16T15:51:59.000Z","dependencies_parsed_at":"2024-01-13T16:42:44.199Z","dependency_job_id":"9856e293-8adf-4b4c-a694-ef5e9cd4db6d","html_url":"https://github.com/tchapi/own-private-cloud","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tchapi%2Fown-private-cloud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tchapi%2Fown-private-cloud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tchapi%2Fown-private-cloud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tchapi%2Fown-private-cloud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tchapi","download_url":"https://codeload.github.com/tchapi/own-private-cloud/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230471175,"owners_count":18231193,"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":["cloud","containers","docker","open-source"],"created_at":"2024-10-01T18:59:46.389Z","updated_at":"2025-08-20T23:32:49.376Z","avatar_url":"https://github.com/tchapi.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Personal Infrastructure As A Service\n\n✅ See [this blogpost](https://www.foobarflies.io/your-own-public-cloud-why-not/) and [this follow-up](https://www.foobarflies.io/a-year-with-my-own-public-cloud/) for a complete (and technical) explanation.\n\nServices :\n\n  - Authelia — an Apache 2.0 OIDC provider\n  - Filebrowser — An Apache 2.0 simple web files browser / uploader and sharing interface\n  - Passbolt — A free, open-source, extensible, OpenPGP-based password manager\n  - Linkding — A MIT simple bookmarking service\n  - Davis — A MIT WebDAV, CalDAV and CardDAV server, based on sabre/dav\n  - kvtiles — An open-source map tiles server in Go, Apache 2.0 License\n  - Docker Mailserver — a MIT fullstack mail server\n  - Snappymail — an AGPLv3 simple and lightweight webmail forked from RainLoop\n  - Gitea — a MIT self-hosted git service with a web UI\n  - Outline — a BSL 1.1 knowledge base app with a web UI, _akin to Notion_\n\n\u003e All services are served through the Træfik reverse-proxy, certificates are provided by Let's Encrypt, and renewed automatically via Træfik.\n\n# Installation\n\n## Source the env vars needed for OpenStack\n\n    source openrc.sh\n\n## Create the machine\n\n    docker-machine create -d openstack \\\n    --openstack-flavor-name=\"b2-7\" \\\n    --openstack-region=\"GRA5\" \\\n    --openstack-image-name=\"Debian 9\" \\\n    --openstack-net-name=\"Ext-Net\" \\\n    --openstack-ssh-user=\"debian\" \\\n    --openstack-keypair-name=\"MY_KEY_NAME_IN_OPENSTACK\" \\\n    --openstack-private-key-file=\"/path/to/.ssh/id_rsa\" \\\n    default\n\n## Install necessary packages on the host \n\n    docker-machine ssh default 'sudo apt update \u0026\u0026 sudo apt install -y -f software-properties-common fail2ban haveged'\n\n  - `software-properties-common` is a common package providing standard libs\n  - `fail2ban` is to prevent unwanted access\n  - `haveged` is for Passbolt - to generate entropy\n\n\u003e Note: if you don't use `docker-machine`, you can just SSH to the host normally too\n\n## Mount external attached block storage volume\n\n\u003e The volumes must be attached beforehand in the OpenStack console\n\n#### The databases volume :\n\n    docker-machine ssh default 'sudo fdisk /dev/sdb # n, p, w'\n    docker-machine ssh default 'sudo mkfs.ext4 /dev/sdb1'\n    docker-machine ssh default 'sudo mkdir /mnt/databases \u0026\u0026 sudo mount /dev/sdb1 /mnt/databases'\n    docker-machine ssh default 'sudo mkdir /mnt/databases/mysql /mnt/databases/pgsql /mnt/databases/filebrowser'\n\n#### The files volume :\n\n    docker-machine ssh default 'sudo fdisk /dev/sdc # n, p, w'\n    docker-machine ssh default 'sudo mkfs.ext4 /dev/sdc1'\n    docker-machine ssh default 'sudo mkdir /mnt/files \u0026\u0026 sudo mount /dev/sdc1 /mnt/files'\n    docker-machine ssh default 'sudo mkdir /mnt/files/filebrowser /mnt/files/mails/data /mnt/files/mails/state /mnt/files/gitea /mnt/files/passbolt /mnt/files/webdav /mnt/files/linkding /mnt/files/outline'\n\n## Get environment variables to target the remote docker instance\n\n    eval $(docker-machine env default)\n\n### Alternatively, you can create a context via SSH:\n\nUse the host user and name to create a new context (you can name it how you like, I used `cloud` here):\n\n    docker context create cloud --docker \"host=ssh://ubuntu@my-cloud.me\"\n\nThen, you just have to `docker context use cloud` before being able to run commands as usual.\n\n\u003e You will find all your contexts with `docker context ls` :\n\u003e\n\u003e     $ docker context ls\n\u003e     NAME                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR\n\u003e     cloud *                                                       ssh://ubuntu@my-cloud.me\n\u003e     default             Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm\n\n\u003e Pay attention! `docker-compose` does not know of contexts ...\n\n## Init all submodules to retrieve up to date code\n\n    git submodule update --init\n\n\u003e When rebuilding, don't forget to update submodules with `git submodule update --recursive --remote`\n\n## Build all custom images\n\nBuild configuration files first (_so that environment variables are replaced correctly_):\n\n    ./scripts/build-configuration-files.sh\n\nAnd then build the images :\n\n    docker compose build\n\n\u003e If you want to extend the Docker Compose services definitions, you can create an addendum `docker-compose.supplementary.yaml` file for instance, and run `docker compose` using both files to merge the configurations:\n\u003e \n\u003e     docker compose -f docker-compose.yaml -f docker-compose.supplementary.yaml ps\n\u003e\n\u003e You can check that your configuration is merged correctly with:\n\u003e \n\u003e     docker compose -f docker-compose.yaml -f docker-compose.supplementary.yaml config\n\u003e   \n\u003e See [this Medium post](https://pscheit.medium.com/docker-compose-advanced-configuration-541356d121de) for more details\n\n## Provision the whole thing in daemon mode\n\n    docker compose up -d\n\n🎉\n\n## Create the Passbolt admin user\n\n    ./scripts/passbolt/init-admin-user.sh\n\n## Create the Gitea admin user\n\n    ./scripts/gitea/init-admin-user.sh\n\n## Create the Linkding single user\n\n    ./scripts/linkding/init-user.sh\n\n## Copy the custom template files for Gitea\n\nThese files resides in `configurations/gitea`; copy the `public` and `templates` folders to `/mnt/files/gitea/gitea/.` before provisionning the container, or restart it after doing it.\n\n\u003e **How to enable SSH passthrough for Gitea**\n\u003e \n\u003e If you want to be able to use the standard port 22 for git, you need to create a passthrough between your Docker host and the gitea container. In order to do so, you have many options as outlined in https://docs.gitea.io/en-us/install-with-docker/#ssh-container-passthrough.\n\u003e \n\u003e The container is setup for the first option (the shim), and you need to run `./scripts/gitea/init-ssh-passthrough.sh` **on your host** if you want to set it up in full. Be wary that the UID and GID used are `2022` and if you want to change it, you need to do it both in the `docker-compose.yml` file and in this script.\n\u003e\n\u003e If all succeeds, you will be able to test the SSH connection with `ssh -T git@${GIT_DOMAIN}` and you will be granted a message like so:\n\u003e    \n\u003e    _Hi there, {your_username}! You've successfully authenticated with the key named {your_ssh_key_name}, but Gitea does not provide shell access._\n\n## Init the davis instance if needed (_if the tables do not already exist_)\n\n    ./scripts/davis/init-mysql-tables.sh\n\n## And finally, create a rule so that all the traffic of mail containers (SMTPD mainly) goes out by the `MAIL_HOST_IP` defined in your `.env` file\n\n    ./scripts/mail/create-iptables-rule.sh\n\n\u003e ⚠️ WARNING ⚠️ : On Debian Buster (10), `iptables` now uses `nft` under the hood, and it just **doesn't work** in this case. You need to select the legacy iptables via `update-alternatives --config iptables` first, restart the Docker engine, and recreate the networks (_so that the rules are re-applied_) before playing the script above. See for instance https://github.com/docker-mailserver/docker-mailserver/issues/1356.\n\n# Automatic backups\n\nIn the event of a burning datacenter, you might want to backup all your data to some other provider / server so that you can recover (most of) your data.\n\nWe're going to _incrementally_ backup `/mnt/database` and `/mnt/files` — that should be sufficient to help us recover from a disaster.\n\nWe use [**duplicity**](http://duplicity.nongnu.org/index.html) for this, and a S3-compatible backend to store the backups remotely (but with duplicity, you can use pretty much whatever service you want).\n\n\u003e See https://www.scaleway.com/en/docs/store-object-with-duplicity/#-Installing-Software-Requirements for more info on their Object Storage solutions and the way it works with duplicity\n\n## Install Python 3.9.2 and the latest duplicity version\n\nOn the Docker host:\n\n#### Install Python 3.9.2 (if needed)\n\n    sudo apt install --no-install-recommends wget build-essential libreadline-gplv2-dev libncursesw5-dev \\\n     libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev\n\n    wget https://www.python.org/ftp/python/3.9.2/Python-3.9.2.tgz\n    tar xzf Python-3.9.2.tgz\n    cd Python-3.9.2\n    ./configure --enable-optimizations\n    sudo make install # with 'sudo', we replace the original Python provided with the distro\n\n#### Install Duplicity requirements\n\n    sudo apt update \u0026\u0026 sudo apt install -y -f gettext librsync-dev\n\n#### Compile and install Duplicity with latest Python3.9 (_that we previously installed_)\n\n    wget https://launchpad.net/duplicity/0.8-series/0.8.21/+download/duplicity-0.8.21.tar.gz\n    tar xaf duplicity-0.8.21.tar.gz\n    cd duplicity-0.8.21\n    pip3 install -r requirements.txt\n    pip3 install boto # for S3 remote target\n    sudo python3 setup.py install\n\n\u003e You must create a `/root/.aws/credentials` file with your S3 credentials:\n\u003e\n\u003e     [default]\n\u003e     aws_access_key_id=EXAMPLE_KEY\n\u003e     aws_secret_access_key=EXAMPLE_SECRET\n\u003e\n\u003e The user in which \"home\" you set these credentials will need to be the one running the cron task obviously. A simple solution would be to use `root`, since duplicity must be able to read all the files that you want to backup\n\n## Add a crontab for the backup\n\nCreate `/etc/cron.d/backup_daily` with :\n\n    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n    42 01 * * * root duplicity incr --full-if-older-than 365D --volsize 1024 --asynchronous-upload --no-encryption --include /mnt/databases --include /mnt/files --file-prefix \"cloud_\" --exclude '**' /mnt/ s3://\u003cS3_HOST\u003e/\u003cS3_BUCKET_NAME\u003e \u003e\u003e /var/log/duplicity.log 2\u003e\u00261\n\n\u003e This will run every day, at 01:42 AM, as the `root` user.\n\nOptions (see http://duplicity.nongnu.org/vers8/duplicity.1.html):\n\n  - `--volsize 1024` : Use chunks of 1Go\n  - `--asynchronous-upload` : Try to speed up uploads using CPU and bandwidth more efficiently\n  - `--no-encryption` : Do not encrypt remote backups\n  - `--include /mnt/databases --include /mnt/files --exclude '**'` : Only backup `/mnt/files` and `/mnt/databases`\n\n## Bonus: additional cli commands to work on backups\n\n### Make a full backup (in case you need to start fresh)\n\n    duplicity full --volsize 1024 --asynchronous-upload --file-prefix \"cloud_\" --no-encryption --include /mnt/databases --include /mnt/files --exclude '**' --progress /mnt/ s3://\u003cS3_HOST\u003e/\u003cS3_BUCKET_NAME\u003e\n\n#### List all backed-up files\n\n    duplicity list-current-files --file-prefix \"cloud_\" s3://\u003cS3_HOST\u003e/\u003cS3_BUCKET_NAME\u003e\n\n#### Verify data (_in depth_) and its recoverability\n\n    duplicity verify \\\n        --no-encryption \\\n        --include /mnt/databases \\\n        --include /mnt/files \\\n        --file-prefix \"cloud_\" \\\n        --exclude '**' \\\n        --compare-data \\\n        s3://\u003cS3_HOST\u003e/\u003cS3_BUCKET_NAME\u003e /mnt/\n\n#### Move another backup file to S3\n\nWith s3cmd:\n\n    s3cmd put file.zip s3://{bucket}/{path}/file.zip --storage-class=GLACIER --multipart-chunk-size-mb=100\n\n\u003e It's important to set a multipart chunk size so that the original file size divided by the chunk size doesn't exceed 1000 (chunks) since an upload can have at most 1000 chunks.\n\nWith aws cli:\n\n    aws --profile scw_profile s3 cp file.zip s3://{bucket}/{path}/file.zip\n\n\u003e You need to install `pip3 install awscli-plugin-endpoint` and create a profile in  `~/.aws/config` beforehand:\n\u003e\n\u003e    ```\n\u003e    [plugins]\n\u003e    endpoint = awscli_plugin_endpoint\n\u003e    \n\u003e    [profile scw_profile]\n\u003e    region = fr-par\n\u003e    s3 =\n\u003e      endpoint_url = https://s3.fr-par.scw.cloud\n\u003e      multipart_chunksize = 100MB\n\u003e    s3api =\n\u003e      endpoint_url = https://s3.fr-par.scw.cloud\n\u003e    ```\n\n#### Move a file on a S3-compatible storage to a Glacier class\n\nWith s3cmd:\n\n    s3cmd cp s3://{bucket}/{path} s3://{bucket}/{path} --storage-class=GLACIER --add-header=x-amz-metadata-directive:REPLACE\n\nWith aws cli:\n\n    aws s3 cp s3://{bucket}/{path} s3://{bucket}/{path} --storage-class GLACIER\n\n# Updating\n\nUpdate Dockerfiles or the `docker-compose.yml` file, then rebuild the images with `docker compose build`. You can then recreate each container with the newly built images with `docker compose up -d {container}`.\n\nFor some containers using a shared volume such as Davis (`/var/www/davis`) or Snappymail, you need to scrap the underlying volume before updating so that the code is _really_ updated on rebuild.\n\nFor instance:\n\n    docker rm -f davis davis-proxy \u0026\u0026 docker volume rm davis_www\n    docker container prune \u0026\u0026 docker image prune\n    docker compose up -d --force-recreate --build davis-proxy davis\n\nor\n\n    docker rm -f snappymail snappymail-proxy \u0026\u0026 docker volume rm snappymail_www\n    docker container prune \u0026\u0026 docker image prune\n    docker compose up -d --force-recreate --build webmail-proxy webmail\n\n\u003e [!TIP]\n\u003e\n\u003e    For the MySQL container to be updated correctly, you absolutely need to do a complete normal shutdown before bringing the new container up:\n\u003e\n\u003e    1. Update the MySQL version in the `Dockerfile-mysql`` Dockerfile\n\u003e    2. Build the image: `docker compose build mysql`\n\u003e    3. In the actual, running MySQL container, `mysql -uroot -p`\n\u003e    4. Issue a graceful shutdown with `shutdown;` \n\u003e    5. The container will then stop by itself\n\u003e    6. Bring the new container up:  `docker compose up -d`\n\n# SSL\n\nThe given Traefik V2.0 configuration (_SSL params, etc_), along with a proper DNS configuration (including a correct CAA entry — see [here](https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum)), will result in a **A+** rating in [SSLLabs](https://www.ssllabs.com) :\n\n![A+ Rating page](https://raw.githubusercontent.com/tchapi/own-private-cloud/master/_screenshots/ssl_rating.png)\n\n# DNS entries for mail\n\nYou have to add some DNS entries to make your setup work. Run the following scripts to have them listed according to your environment values:\n\n    ./scripts/mail/show-dns-entries.sh\n\n## Test your email server\n\nTest that your SMTP endpoint works as expected:\n\n    openssl s_client -starttls smtp -connect mail.mydomain.com:587\n\nand:\n\n    openssl s_client -connect mail.mydomain.com:465\n\nBoth should yield a prompt, and say that the certificate is ok (`Verify return code: 0 (ok)`)\n\nTest your IMAP endpoint (Dovecot) with:\n\n    openssl s_client -connect mail.mydomain.com:993\n\nYou can try to login with `A LOGIN {user} {password}` by replacing `{user}` and `{password}` with the real strings, which should yield something along those lines:\n\n    A OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE SNIPPET=FUZZY PREVIEW=FUZZY STATUS=SIZE LITERAL+ NOTIFY] Logged in\n\n## Specific mail IP address\n\nWhen using a separate IP for the mail server, we need to add a rule to the `POSTROUTING` chain in the `nat` table to allow traffic originating from the mail network to go through the correct IP; On the Docker host, run:\n\n    sudo iptables -t nat -I POSTROUTING -s 172.100.0.0/16 -j SNAT --to $MAIL_HOST_IP\n\n\u003e Don't forget to set `MAIL_HOST_IP` beforehand if not done already. \"172.100.0.0/16\" is the subnet indicated in the docker-compose.yml file for the mail network.\n\nTo list all the rules in the `nat` table:\n\n    sudo iptables -t nat -L --line-numbers\n\n# Run \u0026 Maintenance\n\nTo see the disk usage :\n\n    docker-machine ssh default \"df -h | grep '^/dev'\"\n\nWhen making a block storage bigger :\n\n  1. First **stop** the container using it (filebrowser for instance, or many more if it's the databases)\n  2. Unmount the `/dev/sd*1` volume\n  3. Change the size in the Public Cloud interface\n  4. WARNING The volume name will likely change\n  4. `sudo fdisk /dev/sd*` (_no number here_): Delete (`d`,`w`) / recreate the partition (`n`,`p`,`w`) / `sudo e2fsck -f /dev/sd*1` / `sudo resize2fs /dev/sd*1`\n  5. Remount it\n  6. Restart the container\n  7. :tada:\n\nSee https://www.cloudberrylab.com/resources/blog/linux-resize-partition/ for more info\n\n# Tips\n\n\u003e If you change databases.sh, you need to clear the content of `/mnt/databases/mysql` (and `couch` too if needed) on the host for the entrypoint script to be replayed entirely\n\n\n### Redirect a domain to another one with Traefik\n\nIt's easy as to add rules to the `traefik` container. Example if you want to redirect `calendar.mydomain.com` to `dav.mydomain.com`:\n\n```yaml\n- \"traefik.http.routers.legacy_calendar_to_dav.rule=Host(`calendar.mydomain.com`)\"\n- \"traefik.http.routers.legacy_calendar_to_dav.service=noop@internal\"\n- \"traefik.http.routers.legacy_calendar_to_dav.middlewares=to_dav\"\n- \"traefik.http.routers.legacy_calendar_to_dav.tls=true\"\n- \"traefik.http.middlewares.to_dav.redirectregex.regex=^https://calendar.mydomain.com/(.*)\"\n- \"traefik.http.middlewares.to_dav.redirectregex.replacement=https://dav.mydomain.com/$${1}\"\n- \"traefik.http.middlewares.to_dav.redirectregex.permanent=true\"\n```\n\n### Username and password for the status page\n\nIn order to create a password for the status page (Traefik's default status page that will reside at https://status.mydomain.com), you need to create a username/password combo with:\n\n```\nhtpasswd -nB username\n\u003e New password: ...\n```\n\n### Add a failover IP on Debian 9\n\nSupposing an alias of `1`, and an interface of `ens3` :\n\nDisable auto configuration on boot by adding :\n\n    network: {config: disabled}\n\nin `/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg`\n\nEdit `/etc/network/interfaces.d/50-cloud-init.cfg` and add :\n\n    auto ens3:1\n    iface ens3:1 inet static\n    address YOUR.FAILOVER.IP\n    netmask 255.255.255.255\n\n### The map tiles server\n\nYou can change the region, just grab a tag at https://hub.docker.com/r/akhenakh/kvtiles/tags, such as `france-13-latest` for instance.\n\nThe tiles server is available directly at https://{MAPS_DOMAIN}/. You can see a handy map at https://{MAPS_DOMAIN}/static/?key={MAPS_API_KEY}.\n\n### How-to rename a docker volume\n\n    echo \"Creating destination volume ...\"\n    docker volume create --name new_volume_name\n    echo \"Copying data from source volume to destination volume ...\"\n    docker run --rm \\\n               -i \\\n               -t \\\n               -v old_volume_name:/from \\\n               -v new_volume_name:/to \\\n               alpine ash -c \"cd /from ; cp -av . /to\"\n\n### How to disable ipv6 on Debian\n\nYou might need this if Traefik does not manage to get certificates with a tls challenge (and if you don't have any ipv6 dns created)\n\n    sysctl -w net.ipv6.conf.all.disable_ipv6=1\n    sysctl -w net.ipv6.conf.default.disable_ipv6=1\n    sysctl -w net.ipv6.conf.lo.disable_ipv6=1\n\n# Literature\n\n  - Docker best practices : https://blog.docker.com/2019/07/intro-guide-to-dockerfile-best-practices/\n  - Nginx Reverse proxy : https://www.thepolyglotdeveloper.com/2017/03/nginx-reverse-proxy-containerized-docker-applications/\n  - nginx TLS / SSL configuration options : https://gist.github.com/konklone/6532544\n  - Lets Encrypt with Docker : https://devsidestory.com/lets-encrypt-with-docker/\n  - Lets Encrypt with Docker (alt) : https://medium.com/@pentacent/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71\n  - Create and configure a block volume in OVH Public Cloud : https://docs.ovh.com/fr/public-cloud/creer-et-configurer-un-disque-supplementaire-sur-une-instance/\n  - Shell command  / Entrypoint in Docker : https://stackoverflow.com/questions/41512237/how-to-execute-a-shell-command-before-the-entrypoint-via-the-dockerfile\n  - Ignore files for Cozy drive : https://github.com/cozy-labs/cozy-desktop/blob/master/doc/usage/ignore_files.md\n  - Deploy your own SAAS : https://github.com/Atarity/deploy-your-own-saas/blob/master/README.md\n  - A set of Ansible playbooks to build and maintain your own private cloud : https://github.com/sovereign/sovereign/blob/master/README.md\n\n## Mails\n\n  - How to run your own mail server : https://www.c0ffee.net/blog/mail-server-guide/\n  - Mail servers are not hard : https://poolp.org/posts/2019-08-30/you-should-not-run-your-mail-server-because-mail-is-hard/\n  - NSA-proof your e-mail in 2 hours : https://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/\n  - Mail-in-a-Box : https://mailinabox.email/\n  - Setting up a mailserver with OpenSMTPD and Dovecot : https://poolp.org/posts/2019-09-14/setting-up-a-mail-server-with-opensmtpd-dovecot-and-rspamd/\n  - OpenSMTPD: Setting up a mailserver : http://z5t1.com:8080/cucumber_releases/cucumber-1.1/source/net-extra/opensmtpd/doc/example1.html\n  - Test a SMTP server : https://www.stevenrombauts.be/2018/12/test-smtp-with-telnet-or-openssl/\n  - A simple mailserver with Docker : https://tvi.al/simple-mail-server-with-docker/\n  - A set of Ansible playbooks to build and maintain your own private cloud : https://github.com/sovereign/sovereign/blob/master/README.md\n  - Setting up an email server in 2020 with OpenSMTPD and Dovecot https://prefetch.eu/blog/2020/email-server/\n  - How to self-host your email server : https://www.garron.blog/posts/host-your-email-server.html\n  - About changing the outgoing address for a network of containers : https://medium.com/@havloujian.joachim/advanced-docker-networking-outgoing-ip-921fc3090b09\n  - An OpenBSD E-Mail Server Using OpenSMTPD, Dovecot, Rspamd, and RainLoop https://www.vultr.com/docs/an-openbsd-e-mail-server-using-opensmtpd-dovecot-rspamd-and-rainloop\n\n## Dockerfiles :\n\n  - Cozy : https://github.com/cozy/cozy-stack/blob/master/docs/INSTALL.md\n  - Passbolt : https://hub.docker.com/r/passbolt/passbolt/\n  - Standard Notes : https://github.com/arugifa/standardnotes-server-docker/blob/master/Dockerfile\n  - MariaDB : https://github.com/docker-library/mariadb/blob/master/10.4/docker-entrypoint.sh\n  - x-browser-sync : https://github.com/xbrowsersync/api-docker\n\n## Other alternatives\n\nSee https://github.com/Kickball/awesome-selfhosted for more awesome self-hosted alternatives.\n\n### Other CalDav / CardDav projects worth noting\n\n  - SoGo : https://sogo.nu/support/faq/how-to-install-sogo-on-debian.html\n  - Radicale : https://radicale.org/\n  - Calendar Server:  https://www.calendarserver.org/ (Apple)\n  - An android client app for CalDav / CardDav : https://gitlab.com/bitfireAT/davx5-ose - https://f-droid.org/packages/at.bitfire.davdroid/\n\n### About the tiles server\n\n  - The blog post : https://blog.nobugware.com/post/2020/free-maps-for-all/\n  - The repository : https://github.com/akhenakh/kvtiles\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftchapi%2Fown-private-cloud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftchapi%2Fown-private-cloud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftchapi%2Fown-private-cloud/lists"}