This is an evolution from https://github.com/ruppel/myserver.
After the os upgrade to debian 12 the system had sever problems. I didn't manage to fix them so I decided to make a complete new setup. A friend recommended mailcow to me, so i gave it a try and .... it is wonderful.
These are the scripts that I used (and use) to configure my running web and mail server. Take them as a blueprint for your needs. Do not blindly clone them and start. Having a web and mail server in the internet needs that you understand what you are doing!!
- Use debian 12 (bookworm) as basis OS
- Keep the system up to date automatically
- Use docker containers
- Use traefik as reverse proxy for the docker containers
- Use letsencrypt with DNS Challenge to get SSL certificates
- Use mailcow dockerized for the mail ecosystem
- Use portainer for the look inside containers if something fails
- Use watchtower to keep the docker images up to date
- Use ansible for (nearly) everything to be setup on the server
Apps added:
- Joplin-Server as Sync Server for Joplin
- Wireguard as VPN entry point
- Nextcloud to store files, sync contacts and calendars (and bookmarks)
- Vaultwarden to store and sync credentials over all my clients
- (more to come)
- Use Visual Studio Code
- Use ansible in a docker container, i.e. podman container
- Use a few simple scripts (Unix Shell, working under MacOS) to start ansible
- Use ansible vault to encrypt my inventory file
- The password is stored in file
.vault-pass.txt
(which is listed in.gitignore
) - The file
ansible.cfg
references that file, so to encrypt your inventory file use./podman-ansible/ansible-vault encrypt inv-server.yml
- I didn't manage to get implicit vault decryption working in docker container ansible...
- so before you use your inventory you should decrypt it with
./podman-ansible/ansible-vault decrypt inv-server.yml
- so before you use your inventory you should decrypt it with
- The password is stored in file
Check the scripts! You need to understand what's going on. Use the scripts at your own risk! I do not take any responsibility for damages or data losses! As said before, in my opinion an administrator of a internet facing server should ALWAYS know, what he is doing!
You might install ansible on your desktop computer.
I prefer using the ansible docker image cytopia/ansible:latest-tools
and instead of calling ansible-playbook ...
I use my script here ./podman-ansible/ansible-playbook ...
Things can be sooooo easy :-)
Podman sometime looses the link to the volume. If podman returns
Error: statfs /Users/xxxx/.ssh: no such file or directory
do a
podman machine stop; podman machine start
This is based on Debian Bookworm (12) installation. There are tutorials on how to install that.
- Install ansible
- See
010-install-ansible-controller.sh
for simple script that installs pipx and ansible on debian
- See
If you want to use a user other than root:
- Connect to host using ssh with root user
- Install sudo:
apt install sudo
- Have a non-root user on the host, who is allowed to call
sudo
(On debianadduser username
, give password and data and do ausermod -aG sudo username
) - Additionally connect to host using your now user
username
For the root user, or the user you created above:
- Copy your public ssh key to file
~/.ssh/authorized_keys
to use Public/Private Key SSH Connection - Do the ssh connection to the server (to check if it works and to get the fingerprint of the server)
- Copy file
inv-example.yml
toinv-myserver.yml
- Change values in
inv-myserver.yml
to your needs, for the first test you needansible_host
ansible_user
andansible_become_password
to be set correctly
- Set
ansible_port
to 22, which is the default ssh port. Or directly to the port you configured your server
ansible-playbook -i inv-myserver.yml pingtest.yml
It should return a "OK"
- Change vars in file
inv-myserver.yml
to your needs. Especially you are encouraged to change the ssh port to something other than 22, take 22401 for example.
ansible-playbook -i inv-myserver.yml 020-ensure-target-ssh-port.yml
- This checks the ssh port and changes it if needed
ansible-playbook -i inv-myserver.yml pingtest.yml
- This is the same test above, but it's now using the other ssh port.
- Check the SSH connection to the new port with a terminal, also.
ansible-playbook -i inv-myserver.yml 030-set-the-stage.yml
- This sets the hostname, enhances the sources for apt, installs required ansible tools, updates the system, does a restart, changed dash to bash and disabled sendmail
ansible-playbook -i inv-myserver.yml 040-install-service-apps.yml
- This will install ntp, automatic security updates, fail2ban, docker, traefik, portainer and a docker demo nginx app
- Check, whether you can access the docker demo app in your browser
- Wait a while to see, whether traefik and letsencrypt obtained a certificate (might take few minutes)
- Check whether traefik and portainer are running
- Give Portainer an admin password.
- If you are too late, then restart the stack using
docker compose down; docker compose up -d
on the server in the folder/opt/portainer/
- If you are too late, then restart the stack using
- Only move to the next step, if all works
ansible-playbook -i inv-myserver.yml 050-install-mailcow.yml
- This will install and startup mailcow (and all containers in that family)
- Check, whether you can access the webinterface of mailcow (may take a few seconds)
- Login using startpassword (see https://docs.mailcow.email/i_u_m/i_u_m_install/#start-mailcow)
- Change your Password
- Add "127.0.0.0/8" (as well as your local ip and or your dyndns-domain) to the fail2ban whitelist
- Go to: System > Configuration > Options > Fail2Ban parameters
- Check the logs (i.e. using portainer) whether there is still a problem
- Add E-mail Domains and Mailboxes (of course check out the mailcow documentation for further installation steps, e.g. RSPAMD password)
Congrats! You have a working mailserver!
These are my additional application. Use or don't use them, as you like.
ansible-playbook -i inv-myserver.yml 060-install-additional-apps.yml
This will install- Joplin Server
- Wireguard
- Nextcloud
- Vaultwarden
- Watchtower
- Configure your DNS as documented in https://docs.mailcow.email/prerequisite/prerequisite-dns/
- Have entries for DKIM and DMARC
- (let the servers a little time to syncronize over the world...)
Information on how to move all your services from Server A-SRC to Server B-DST
- A-SRC and B-DST should have been setup using the scripts from here
- You have a working inventory file for A-SRC (e.g.
inv-A-SRC.yml
) and for B-DST (e.g.inv-B-DST.yml
) - Data in B-DST will get lost! (You might want to backup them...)
- The scripts actually backup and restore the following apps
- traefik
- mailcow
- joplin
- wireguard
- vaultwarden
- nextcloud
- Check if you have the same versions of that apps in A-SRC and B-DST
- For joplin: Force the clients to "Re-upload local data to sync target"
- This can be found in Preferences > Synchronization > Show Advanced Settings
- (I needed days to find out, that this is necessary)
- The scripts will create a SSH-Key on the B-DST server and transfer it to the A-SRC server. Having that we cat SFTP the data from there without a password
- Setup the destination server using the scripts above
- We need a direct file transfer from A-SRC to B-DST, so ensure to have a ssh-key on B-DST:
ansible-playbook -i inv-B-DST.yml 101-MOVE-prepare-dst.yml
- Add this key to the authorized keys in A-SRC with command
ansible-playbook -i inv-A-SRC.yml 102-MOVE-prepare-src.yml
- now you should be able to SSH from B-DST to A-SRC. Check this out, using the correct port
- really do this to get the fingerprint of the server
- Now we create the backup bundle using
ansible-playbook -i inv-A-SRC.yml 110-MOVE-backup-src.yml
- Copy the backup to the destination and restore the data there
ansible-playbook -i inv-B-DST.yml 120-MOVE-restore-dst.yml
- MAILCOW... BY NOW, AUTOMATED RESTORE DOESN'T WORK
- You need to restore manually
- Shell in B-DST
cd /opt/mailcow
./helper-scripts/backup_and_restore.sh restore
- Asked for the backup location give the value from the inventory file
inv-B-DST.yml
forbackup.restore_from
followed by/mailcow/
- Answer the other questions
- Restore point should be only one available, so answer 1
- Dataset to restore, answer 0 to restore all
- Force a resync,... (I don't know, but I think it is a good idea), so I answer y
- Stop and restart mailcow ==> answer y
- After that I shutdown mailcow using
docker compose down
and restart it usingdocker compose up -d
- This should restore mailcow completely without error messages
- Check, if everything is working and no data is missing