diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c0baf1e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+backups
diff --git a/README.md b/README.md
index df3bcc0..2c91616 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,45 @@
# Services
Vous trouverez dans ce dépôt l'ensemble des services Open Source que je propose.
+
+## Commandes
+
+### Docker et Docker Compose
+
+L'ensemble des services est géré avec [Docker][docker] et [Docker Compose][docker-compose].
+
+Voici les commandes de base :
+
+- `docker-compose up -d` : Permet de lancer un service
+- `docker-compose down -v` : Permet de supprimer un service (avec l'option -v on supprime aussi les volumes donc attention de pas perdre des données)
+
+### ./run
+
+Vous pourrez trouver dans les dossiers des services un script bash `run`. Le principe est de faciliter la maintenance de chaque service.
+
+Vous pouvez lancer le script sans paramètre pour afficher la documentation du script.
+
+## Liste des services
+
+### Pour les devs
+
+- [PostgreSQL][postgres] : PostgreSQL est un système de gestion de base de données relationnelle et objet.
+- [Gitea][gitea] : Un service Git auto-hébergé très simple à installer et à utiliser. Il est similaire à GitHub, Bitbucket ou Gitlab.
+
+### Pour les ops
+
+- [Traefik][traefik] : Traefik, un reverse-proxy pour vos conteneurs
+
+## Informations
+
+### `set -eu` ?
+
+En haut de chaque script il y a `set -eu` qui veut dire :
+- -e Exit immediately if a command exits with a non-zero status.
+- -u Treat unset variables as an error when substituting.
+
+[docker]: https://docs.docker.com/engine/
+[docker-compose]: https://docs.docker.com/compose/
+[postgres]: ./postgres
+[gitea]: ./gitea
+[traefik]: ./traefik
diff --git a/gitea/.env b/gitea/.env
new file mode 100644
index 0000000..4551f09
--- /dev/null
+++ b/gitea/.env
@@ -0,0 +1,29 @@
+COMPOSE_FILE=../postgres/docker-compose.yml:./docker-compose.yml:./docker-compose.override.yml
+
+# APP
+
+GITEA_IMAGE=gitea/gitea:1.11.5
+GITEA_CONTAINER_NAME=gitea
+GITEA_VOLUME_NAME=gitea
+GITEA_PROTOCOL=http
+GITEA_DOMAIN=gitea.lan
+
+# APP CONFIG
+# https://docs.gitea.io/en-us/install-with-docker/#environments-variables
+
+DISABLE_SSH=true
+RUN_MODE=prod
+ROOT_URL=${GITEA_PROTOCOL}://${GITEA_DOMAIN}
+DISABLE_REGISTRATION=true
+DISABLE_GRAVATAR=true
+#INSTALL_LOCK=true
+
+# DATABASE
+# Voir la description ../postgres/README.md
+
+POSTGRES_IMAGE=postgres:12.2-alpine
+POSTGRES_USER=user-example
+POSTGRES_PASSWORD=password-example
+POSTGRES_DB=postgres-database-name-example
+POSTGRES_CONTAINER_NAME=gitea-postgres
+POSTGRES_VOLUME_NAME=gitea-postgres
diff --git a/gitea/README.md b/gitea/README.md
new file mode 100644
index 0000000..407e9d6
--- /dev/null
+++ b/gitea/README.md
@@ -0,0 +1,28 @@
+# Gitea
+
+> Gitea est un service Git auto-hébergé très simple à installer et à utiliser. Il est similaire à GitHub, Bitbucket ou Gitlab.
+>
+> [Documentation][documentation]
+
+## Configuration
+
+TODO
+
+## Commandes
+
+```sh
+$ ./run
+./run backup : Lancement de la sauvegarde de Gitea
+./run restore : Restoration de la sauvegarde de Gitea
+```
+
+## Liens
+
+- [Site internet][site]
+- [Code source][source]
+- [Docker Hub][dockerhub]
+
+[documentation]: https://docs.gitea.io/fr-fr/
+[site]: https://gitea.io
+[source]: https://github.com/go-gitea/gitea
+[dockerhub]: https://hub.docker.com/r/gitea/gitea
diff --git a/gitea/docker-compose.override.yml b/gitea/docker-compose.override.yml
new file mode 100644
index 0000000..06972df
--- /dev/null
+++ b/gitea/docker-compose.override.yml
@@ -0,0 +1,6 @@
+version: "3.8"
+
+services:
+ gitea:
+ ports:
+ - "3000:3000"
diff --git a/gitea/docker-compose.traefik.yml b/gitea/docker-compose.traefik.yml
new file mode 100644
index 0000000..08dc2f6
--- /dev/null
+++ b/gitea/docker-compose.traefik.yml
@@ -0,0 +1,16 @@
+version: "3.8"
+
+networks:
+ default:
+ name: ${TRAEFIK_NETWORK_NAME}
+
+services:
+ gitea:
+ labels:
+ traefik.enable: 'true'
+ traefik.docker.network: ${TRAEFIK_NETWORK_NAME}
+
+ traefik.http.routers.gitea.rule: 'Host(`${GITEA_DOMAIN}`)'
+ traefik.http.routers.gitea.entrypoints: 'web'
+
+ traefik.http.services.gitea.loadbalancer.server.port: '3000'
diff --git a/gitea/docker-compose.yml b/gitea/docker-compose.yml
new file mode 100644
index 0000000..ac0b7c7
--- /dev/null
+++ b/gitea/docker-compose.yml
@@ -0,0 +1,25 @@
+version: "3.8"
+
+volumes:
+ gitea:
+ name: ${GITEA_VOLUME_NAME}
+
+services:
+ gitea:
+ container_name: ${GITEA_CONTAINER_NAME}
+ image: ${GITEA_IMAGE}
+ restart: always
+ environment:
+ # - USER_UID=1000
+ # - USER_GID=1000
+ DB_TYPE: postgres
+ DB_HOST: postgres:5432
+ DB_NAME: ${POSTGRES_DB}
+ DB_USER: ${POSTGRES_USER}
+ DB_PASSWD: ${POSTGRES_PASSWORD}
+ volumes:
+ - gitea:/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ depends_on:
+ - postgres
diff --git a/gitea/run b/gitea/run
new file mode 100755
index 0000000..b93dd62
--- /dev/null
+++ b/gitea/run
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+set -eu
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+. $DIR/../help.sh
+. $DIR/../postgres/run --only-source
+
+gitea_help() {
+ echo "./run backup : Lancement de la sauvegarde de Gitea"
+ echo "./run restore : Restoration de la sauvegarde de Gitea"
+}
+
+gitea_backup() {
+ script_env
+ BACKUP_DATE_DEFAULT=`date +%Y%m%d_%H%M%S`
+ BACKUP_DATE=${BACKUP_DATE:-$BACKUP_DATE_DEFAULT}
+ backup_folder_create
+
+ POSTGRES_BACKUP_FILE=backups/${BACKUP_DATE}_gitea_postgres.sql
+ postgres_backup
+
+ docker-compose run --rm -v $GITEA_VOLUME_NAME:/data -v $HOME/backups/gitea:/backup gitea tar cvf /backup/${BACKUP_DATE}_gitea_data.tar /data
+}
+
+gitea_restore() {
+ script_start
+ script_env
+ echo "🏁 Start restore PostgreSQL database '$POSTGRES_DB' from '$POSTGRES_BACKUP_FILE'"
+ cat $POSTGRES_BACKUP_FILE | docker exec -i $POSTGRES_CONTAINER_NAME psql -U $POSTGRES_USER -d $POSTGRES_DB
+ script_end
+}
+
+if [ $# -ge 1 ]; then
+ if [ "${1}" == "backup" ]; then
+ script_start
+ gitea_backup
+ script_end
+ elif [ "${1}" == "restore" ]; then
+ script_start
+ gitea_restore
+ script_end
+ elif [ "${1}" != "--only-source" ]; then
+ gitea_help
+ fi
+else
+ gitea_help
+fi
diff --git a/help.sh b/help.sh
new file mode 100755
index 0000000..8c5ce5b
--- /dev/null
+++ b/help.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -eu
+
+script_start() {
+ START=`date +%s`
+}
+
+script_end() {
+ END=`date +%s`
+ echo
+ echo "✨ Done in $((END-START))s"
+}
+
+script_env() {
+ ENV_FILE_DEFAULT=.env
+ ENV_FILE=${ENV_FILE:-$ENV_FILE_DEFAULT}
+ source $ENV_FILE
+}
+
+backup_folder_create() {
+ mkdir -p backups
+}
diff --git a/postgres/.env b/postgres/.env
new file mode 100644
index 0000000..3ca0381
--- /dev/null
+++ b/postgres/.env
@@ -0,0 +1,6 @@
+POSTGRES_IMAGE=postgres:12.2-alpine
+POSTGRES_USER=user-example
+POSTGRES_PASSWORD=password-example
+POSTGRES_DB=postgres-database-name-example
+POSTGRES_CONTAINER_NAME=postgres
+POSTGRES_VOLUME_NAME=postgres
diff --git a/postgres/README.md b/postgres/README.md
new file mode 100644
index 0000000..45f43f2
--- /dev/null
+++ b/postgres/README.md
@@ -0,0 +1,35 @@
+# PostgreSQL
+
+> PostgreSQL est un système de gestion de base de données relationnelle et objet
+>
+> [Wikipédia][wikipedia]
+
+## Configuration
+
+Les variables contenu dans `.env` permettent de changer :
+
+- `POSTGRES_IMAGE` : la version
+- `POSTGRES_USER` : le nom d'utilisateur
+- `POSTGRES_PASSWORD` : le mot de passe
+- `POSTGRES_DB` : le nom de la base de données
+- `POSTGRES_CONTAINER_NAME` : le nom du conteneur
+- `POSTGRES_VOLUME_NAME` : le nom du volume
+
+## Commandes
+
+```sh
+$ ./run
+./run backup : Lancer la sauvegarde d'une base de données
+./run restore : Restore une sauvegarde
+```
+
+## Liens
+
+- [Site Officiel][site]
+- [Code source][source]
+- [Docker Hub][dockerhub]
+
+[wikipedia]: https://fr.wikipedia.org/wiki/PostgreSQL
+[site]: https://www.postgresql.org/
+[source]: https://git.postgresql.org/gitweb/?p=postgresql.git
+[dockerhub]: https://hub.docker.com/_/postgres
diff --git a/postgres/docker-compose.yml b/postgres/docker-compose.yml
new file mode 100644
index 0000000..128b106
--- /dev/null
+++ b/postgres/docker-compose.yml
@@ -0,0 +1,19 @@
+version: "3.8"
+
+volumes:
+ postgres:
+ name: ${POSTGRES_VOLUME_NAME}
+
+services:
+ postgres:
+ container_name: ${POSTGRES_CONTAINER_NAME}
+ image: ${POSTGRES_IMAGE}
+ restart: always
+ environment:
+ POSTGRES_USER: ${POSTGRES_USER}
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
+ POSTGRES_DB: ${POSTGRES_DB}
+ volumes:
+ - postgres:/var/lib/postgresql/data
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
diff --git a/postgres/run b/postgres/run
new file mode 100755
index 0000000..7e0f286
--- /dev/null
+++ b/postgres/run
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -eu
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+. $DIR/../help.sh
+
+postgres_help() {
+ echo "./run backup : Lancer la sauvegarde d'une base de données"
+ echo "./run restore : Restore une sauvegarde"
+}
+
+postgres_backup() {
+ script_env
+ backup_folder_create
+
+ POSTGRES_BACKUP_FILE_DEFAULT=backups/`date +%Y%m%d_%H%M%S`_$POSTGRES_DB.sql
+ POSTGRES_BACKUP_FILE=${POSTGRES_BACKUP_FILE:-$POSTGRES_BACKUP_FILE_DEFAULT}
+
+ echo "🏁 Start backup PostgreSQL database '$POSTGRES_DB' in '$POSTGRES_BACKUP_FILE'"
+ docker exec -i $POSTGRES_CONTAINER_NAME pg_dump $POSTGRES_DB -U $POSTGRES_USER > $POSTGRES_BACKUP_FILE
+}
+
+postgres_restore() {
+ script_env
+ echo "🏁 Start restore PostgreSQL database '$POSTGRES_DB' from '$POSTGRES_BACKUP_FILE'"
+ docker restart $POSTGRES_CONTAINER_NAME
+ docker exec $POSTGRES_CONTAINER_NAME psql template1 -U $POSTGRES_USER -c "DROP DATABASE $POSTGRES_DB"
+ docker exec $POSTGRES_CONTAINER_NAME psql template1 -U $POSTGRES_USER -c "CREATE DATABASE $POSTGRES_DB with owner $POSTGRES_USER"
+ cat $POSTGRES_BACKUP_FILE | docker exec -i $POSTGRES_CONTAINER_NAME psql -U $POSTGRES_USER -d $POSTGRES_DB
+}
+
+if [ $# -ge 1 ]; then
+ if [ "${1}" == "backup" ]; then
+ script_start
+ postgres_backup
+ script_end
+ elif [ "${1}" == "restore" ]; then
+ script_start
+ postgres_restore
+ script_end
+ elif [ "${1}" != "--only-source" ]; then
+ postgres_help
+ fi
+else
+ postgres_help
+fi
diff --git a/traefik/.env b/traefik/.env
new file mode 100644
index 0000000..367a053
--- /dev/null
+++ b/traefik/.env
@@ -0,0 +1,5 @@
+TRAEFIK_VOLUME_NAME=traefik
+TRAEFIK_NETWORK_NAME=traefik
+TRAEFIK_CONTAINER_NAME=traefik
+TRAEFIK_IMAGE=traefik:v2.2.1
+TRAEFIK_DOMAIN=localhost
diff --git a/traefik/README.md b/traefik/README.md
new file mode 100644
index 0000000..20267ee
--- /dev/null
+++ b/traefik/README.md
@@ -0,0 +1,34 @@
+# Traefik
+
+> Traefik est un reverse-proxy pour vos conteneurs
+>
+> [alterway][alterway]
+
+## Configuration
+
+Les variables contenu dans `.env` permettent de changer :
+
+- `TRAEFIK_VOLUME_NAME` : le nom du volume
+- `TRAEFIK_NETWORK_NAME` :le nom du réseau
+- `TRAEFIK_CONTAINER_NAME` : le nom du conteneur
+- `TRAEFIK_IMAGE` : la version
+- `TRAEFIK_DOMAIN` : le nom de domaine
+
+## TODO
+
+- Mise en place d'un script pour sauvegarder les certificats et la configuration (mais vu que tout peut être généré directement avec docker-compose ça ne sera pas une priorité)
+- Mise en place d'un script pour restaurer une sauvegarde
+- Mettre en place les _metrics_
+- Mettre en place une rotation des logs
+- Mettre en place l'_accesslog_
+
+## Liens
+
+- [Site Officiel][site]
+- [Code source][source]
+- [Docker Hub][dockerhub]
+
+[alterway]: https://blog.alterway.fr/traefik-un-reverse-proxy-pour-vos-conteneurs.html
+[site]: https://traefik.io
+[source]: https://github.com/containous/traefik
+[dockerhub]: https://hub.docker.com/_/traefik
diff --git a/traefik/docker-compose.yml b/traefik/docker-compose.yml
new file mode 100644
index 0000000..7176133
--- /dev/null
+++ b/traefik/docker-compose.yml
@@ -0,0 +1,45 @@
+version: "3.8"
+
+volumes:
+ traefik:
+ name: ${TRAEFIK_VOLUME_NAME}
+
+networks:
+ traefik:
+ name: ${TRAEFIK_NETWORK_NAME}
+
+services:
+ traefik:
+ container_name: ${TRAEFIK_CONTAINER_NAME}
+ image: ${TRAEFIK_IMAGE}
+ restart: always
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ - ${TRAEFIK_VOLUME_NAME}:/traefik
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ ports:
+ - target: 80
+ published: 80
+ protocol: tcp
+ mode: host
+ networks:
+ - ${TRAEFIK_NETWORK_NAME}
+ command:
+ - --api.insecure=true
+ - --log.level=INFO
+ - --global.sendanonymoususage=false
+ - --global.checknewversion=false
+
+ - --providers.docker
+ - --providers.docker.exposedbydefault=false
+ - --providers.docker.network=${TRAEFIK_NETWORK_NAME}
+
+ - --entrypoints.web.address=:80
+ labels:
+ traefik.enable: 'true'
+
+ traefik.http.routers.traefik.rule: 'Host(`${TRAEFIK_DOMAIN}`)'
+ traefik.http.routers.traefik.entrypoints: 'web'
+
+ traefik.http.services.traefik.loadbalancer.server.port: '8080'