Skip to content
Your own private cloud services with Docker
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Personal Infrastructure As A Service

See this blogpost for a complete (and technical) explanation.

Services :

  • Standard notes — A free, open-source, and completely encrypted notes app
  • Cozy Cloud (Drive and settings only) — A smart personal cloud to gather all your data
  • Passbolt — A free, open-source, extensible, OpenPGP-based password manager
  • X-browser Sync — A free and open-source browser syncing tool
  • Baïkal — A GPLv3 Cal and CardDAV server, based on sabre/dav
  • Wekan — A MIT Kanban board manager, comparable to Trello
  • Syncthing — A continuous file synchronization program under the Mozilla Public License 2.0 license

All services are served through an HTTPS proxy based on Nginx, certificates are provided by Let's Encrypt.


Source the env vars needed for OpenStack


Create the machine

docker-machine create -d openstack \
--openstack-flavor-name="b2-7" \
--openstack-region="GRA5" \
--openstack-image-name="Debian 9" \
--openstack-net-name="Ext-Net" \
--openstack-ssh-user="debian" \
--openstack-keypair-name="MY_KEY_NAME_IN_OPENSTACK" \
--openstack-private-key-file="/path/to/.ssh/id_rsa" \

Install necessary packages on the host (for passbolt - to generate entropy)

docker-machine ssh default 'sudo apt update && sudo apt install -y -f haveged'

Mount external attached block storage volume

The volumes must be attached beforehand in the OpenStack console

The databases volume :

docker-machine ssh default 'sudo fdisk /dev/sdb # n, p, w'
docker-machine ssh default 'sudo mkfs.ext4 /dev/sdb1'
docker-machine ssh default 'sudo mkdir /mnt/databases && sudo mount /dev/sdb1 /mnt/databases'
docker-machine ssh default 'sudo mkdir /mnt/databases/mysql /mnt/databases/couch /mnt/databases/mongo'

The files volume :

docker-machine ssh default 'sudo fdisk /dev/sdc # n, p, w'
docker-machine ssh default 'sudo mkfs.ext4 /dev/sdc1'
docker-machine ssh default 'sudo mkdir /mnt/files && sudo mount /dev/sdc1 /mnt/files'
docker-machine ssh default 'sudo mkdir /mnt/files/cozy /mnt/files/sync'

Get environment variables to target the remote docker instance

eval $(docker-machine env default)

Init all submodules to retrieve up to date code

git submodule update --init

Build all custom images

Build configuration files first (so that environment variables are replaced correctly):


And then build the images :

docker-compose build

Set the dummy SSL certificates, then launch nginx and retrieve the real certificates


Set the Cozy instance


Provision the whole thing in daemon mode

Some containers have already been started by the init-letsencrypt script This should only start certbot

docker-compose up -d

Create the Passbolt admin user


Init the Baikal instance if needed (if the tables do not already exist)


Run & Maintenance

To prevent user registration in the notes container :

docker exec -it notes sed -i 's/\(post "auth" =>\)/# \1/' /data/src/config/routes.rb
docker-compose restart standardnotes

To prevent user registration in wekan, just go in the settings page (https://{}/setting) and deactivate it.

To see the disk usage :

docker-machine ssh default "df -h | grep '^/dev'"

When making a block storage bigger :

  1. First stop the container using it (cozy + syncthing for instance, or many more if it's the databases)
  2. Unmount the /dev/sd*1 volume
  3. Change the size in the Public Cloud interface
  4. WARNING The volume name will likely change
  5. Delete (d,w) / recreate the partition (n,p,w) / sudo e2fsck -f /dev/sd*1 / sudo resize2fs /dev/sd*1
  6. Remount it
  7. Restart the container
  8. 🎉

See for more info


If you change, you need to clear the content of /mnt/databases/mysql (mongo, or couch too if needed) on the host for the entrypoint script to be replayed entirely

How-to rename a docker volume

echo "Creating destination volume ..."
docker volume create --name new_volume_name
echo "Copying data from source volume to destination volume ..."
docker run --rm \
           -i \
           -t \
           -v old_volume_name:/from \
           -v new_volume_name:/to \
           alpine ash -c "cd /from ; cp -av . /to"


Dockerfiles :

Other alternatives

See for more awesome self-hosted alternatives.

Other CalDav / CardDav projects worth noting

You can’t perform that action at this time.