avys

Reader

Read the latest posts from avys.

from saint

Monitoring best practices

There are things that really irks my day and monitoring alerts that are not actionable (i.e. – informational) are in that list, so writing this blog post to improve the signal.

  • Any alert you receive from monitoring system should be actionable and that action must not be snoosable or something similar.

You get alert – you work bitch.

 
Read more...

from saint

How to use Borgmatic to backup PostgreSQL in Kubernetes

There are many goodies in the internets, but not much good documentation so that usage of them would be frictionless. Here is a short writeup on backuping up Mastodon instance database, running on Kubernetes with Borgmatic, but it could be used for any generic database and/or file path, that is supported by Borg backup and Borgmatic.

For the destination/target repository I am using Borg Base, but it should work with any Borg compatible ssh repo.

There are some issues that I might solve in the future, namely – storing sensitive information in safer way, but for the moment I just wanted to make backups work. If you will do it – please let me know and I will update my setup.

First things first, we need: – Kubernetes – Mastodon deployment in its own namespace – Setup SSH keys – Setup Healtchecks – Setup Borgbase – Install Borgmatic via Helm Chart, using our values

Kubernetes

You should be runnning something already

Mastodon deployment

You should have deployed Mastodon already in its own namespace. Instructions should work for any generic deployment that uses PostgreSQL or any other supported database.

Setup SSH keys

You need to generate ssh key and save both parts – private and public. Private will go to our Borgmatic setup and public – to Borgbase.

ssh-keygen -t ed25519 -f /tmp/id_ed25519

This can be done on any computer that has ssh-keygen utility. /tmp/id_ed25519will contain the private part and /tmp/id_ed25519.pub – the public. You don't need the files themselves, just contents.

Setup Healthchecks

This is optional, but do it if you do not use Borgbase – get https://healthchecks.io/ account – it is free for small numbers of checks. Borgbase also has the same functionality, so you might skip it if you chose to use Borgbase.

Setup Borgbase

Again – you can use whatever repository server that supports Borg, but I found Borgbase having all the features I require for the cheap price. Add public ssh key part to keys and make sure that repository is tied to this key. Save the repository address.

Install Borgmatic via Helm Chart, using our values

I have used this Chart – https://charts.gabe565.com/charts/borgmatic/ – it is great stuff, just had some issues figuring out how it works. I am kinda slow learner – I make a lot of assumptions that result in being me wrong most of the time. The trick was to find good values for values.yaml:

#
# IMPORTANT NOTE
#
# This chart inherits from our common library chart. You can check the default values/options here:
# https://github.com/bjw-s/helm-charts/blob/main/charts/library/common/values.yaml
#

image:
  # -- image repository
  repository: ghcr.io/borgmatic-collective/borgmatic
  # -- image pull policy
  pullPolicy: IfNotPresent
  # -- image tag
  tag: 1.7.14

controller:
  # -- Set the controller type. Valid options are `deployment` or `cronjob`.
  type: deployment
  cronjob:
    # -- Only used when `controller.type: cronjob`. Sets the backup CronJob time.
    schedule: 0 * * * *
    # -- Only used when `controller.type: cronjob`. Sets the CronJob backoffLimit.
    backoffLimit: 0

# -- environment variables. [[ref]](https://borgbackup.readthedocs.io/en/stable/usage/general.html#environment-variables)
# @default -- See [values.yaml](./values.yaml)
env:
  # -- Borg host ID used in archive names
  # @default -- Deployment namespace
  BORG_HOST_ID: ""
  BORG_PASSPHRASE: ---GENERATE THIS WITH pwgen OR SIMILAR TOOL---
  PGPASSWORD: ---PostgresSQL db password---
  # MYSQL_PWD:
  # MONGODB_PASSWORD:

persistence:
  # -- Configure persistence settings for the chart under this key.
  # @default -- See [values.yaml](./values.yaml)
  data:
    enabled: false
    retain: true
    # storageClass: ""
    # accessMode: ReadWriteOnce
    # size: 1Gi
    subPath:
      - path: borg-repository
        mountPath: /mnt/borg-repository
      - path: config
        mountPath: /root/.config/borg
      - path: cache
        mountPath: /root/.cache/borg
  # -- Configure SSH credentials for the chart under this key.
  # @default -- See [values.yaml](./values.yaml)
  ssh:
    name: borgmatic-ssh 
    enabled: true
    type: configMap
    mountPath: /root/.ssh/
    readOnly: false
    defaultMode: 0600

configMaps:
  # -- Configure Borgmatic container under this key.
  # @default -- See [values.yaml](./values.yaml)
  ssh :
    enabled: true
    data:
      id_ed25519: |
        -----BEGIN OPENSSH PRIVATE KEY-----
        --- PASTE YOUR PRIVATE KEY HERE---
        -----END OPENSSH PRIVATE KEY-----
      known_hosts: |
        --- paste the output of ssh-keyscan borg-repository-address ---
      
  config:
    enabled: true
    data:
      # -- Crontab
      crontab.txt: |-
        0 1 * * * PATH=$PATH:/usr/bin /usr/local/bin/borgmatic --stats -v 0 2>&1
      # -- Borgmatic config. [[ref]](https://torsion.org/borgmatic/docs/reference/configuration)
      # @default -- See [values.yaml](./values.yaml)
      config.yaml: |
        location:
          # List of source directories to backup.
          source_directories:
            - /etc/ <- any directory you want as we are concerned only with db backup

          # Paths of local or remote repositories to backup to.
          repositories:
            - ---BORG REPOSITORY URL---

        retention:
          # Retention policy for how many backups to keep.
          keep_daily: 7
          keep_weekly: 4
          keep_monthly: 6

        consistency:
          # List of checks to run to validate your backups.
          checks:
            - name: repository
            - name: archives
              frequency: 2 weeks

        hooks:


          # Databases to dump and include in backups.
          postgresql_databases:
            - name: mastodon_production
              hostname: hostname-of-mastodon-postgresql-db
              username: mastodon

          # Third-party services to notify you if backups aren't happening.
          healthchecks: --- healtcheck url ---
helm install borgmatic gabe565/borgmatic -f values.yaml -n mastodon
kubectl rollout status deployment.apps/borgmatic -n mastodon
kubectl exec -i deployment.apps/borgmatic -n mastodon -- borgmatic init --encryption repokey-blake2
kubectl exec -it deployment.apps/borgmatic -n mastodon -- borgmatic create --stats

All of these commands should be completed without errors

 
Read more...

from Ką daryt?

Ne kartą esu matęs, kai programeris prašo priėjimo prie serverio, o adminas, jei geranoriškai nusiteikęs sako – “tai duok savo ssh raktą, pridėsiu prie serverio” ir iškart pamiršta šitą problemą keliom dienom ar net savaitėm. O kur dar pasišaipymas iš serijos – “ne privačią dalį, o viešą – generuok raktą iš naujo”. Košmaras.

Tai kokio čia rakto nori, kas tas viešas reikalas ir privatus ir kodėl negalima paprasčiau – tegu pasako slaptažodį, kurio programeris pažada neužsirašyti į sąsiuvinį ir parodo kaip jungtis prie serverio, kur jau ten skaitys žurnalą (logus) ir drebėdamas, kad netyčia ko nors nepridirbtų, atsijungs nuo serverio.

SSH raktas – kam jo reikia?

Šiaip adminai liaudis tokia, kad nelabai nori dalintis prieiga prie serverio, o ką ten dar šnekėt apie slaptažodį, kuris kurtas per kokią nors stalo žaidimo sesiją ir saugomas kaip nuosava akis. Na, taip pat yra ir kita priežastis, ogi ta, kad dauguma adminų taip pat nenaudoja slaptažodžių jungtis prie serverio, jį saugo slaptažodžių skrynutėse, kad galėtų naudoti ypatingais reikalais – suvesti jį per internetinę konsolę (ilo, ipmi ar pan – apie tai – kitą kartą) arba tada, kai prie serverio prijungtas monitorius su klaviatūra ir negali jungtis per SSH (pvz – nėra interneto).

Tai kaip jie jungias? Tai vat naudodami tą rakčiuką, o tiksliau – raktų porą. Raktų pora – tai tarpusavyje susiję du raktai, vienas viešas – jį galima dalinti, o kitas – privatus – jį reikia saugoti, niekam nerodyti, o dar geriau – apsaugoti slaptažodžiu. Tie raktai tarpusavyje susiję burtų būdu (matematika), kuri sako, kad jei turi slaptą raktą, o serveris turi tavo viešą raktą – tai galima juos kaip puzlę matematiškai sujungti ir taip užtikrinti, kad slapto rakto turėtojui galima jungtis į serverį. Tai va – tuomet nereikia žinoti slaptažodžio, galima tiesiog nukopijuoti rakčiuko .pub dalį į specialią vietą ir jungtis. Kiekvienas vartotojas turėtų turėti savo raktą, o tam, kad adminas nežinotų slaptosios dalies – jis prašo tą raktą sugeneruoti pačiam programuotojui.

Kaip generuojama raktų pora?

Kompas – MacOS

Generuojam RSA tipo raktą (išbandytas, lėtas, didelis, bet daug kur palaikomas):

❯ ssh-keygen -t rsa -b 4096 Generating public/private rsa key pair. Enter file in which to save the key (/Users/saint/.ssh/id_rsa): /tmp/naujasraktas Enter passphrase (empty for no passphrase): Enter same passphrase again: Passphrases do not match. Try again. Enter passphrase (empty for no passphrase): Enter same passphrase again: Passphrases do not match. Try again. Enter passphrase (empty for no passphrase): Enter same passphrase again: Passphrases do not match. Try again. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /tmp/naujasraktas Your public key has been saved in /tmp/naujasraktas.pub The key fingerprint is: SHA256:SJMOzUtU2US5wb0m2O/ihJQx1VUrLZMiV0rl9GgT9BI saint@keisen.local The key's randomart image is: +---[RSA 4096]----+ | ...*+=oE...| | + ...* B O .| | . B o+ * % = | | = +.+* = * | | + S + | | . . . | | . .. | | .. . | | ... | +----[SHA256]-----+

Dabar kaip darysiu daryti nereikėtų, bet parodysiu kuo skiriasi privatus nuo viešo rakto:

Viešas raktas:

❯ head /tmp/naujasraktas.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC+nW2Z+7loNKUXg9tGLS7JHF/uINZjwDL1Bmd/DPL+JOopMPiqKsuJKyZp471udsxqUsMvzOPY2Cu/Q4tdwET5r7auAyA7v2rC2eFFJZwCbxxW1rY+PbENmfUeJw9CrJJF8sxcYoAvtLtwz5fu15jO3EfBE5HnRDlqH49sB5i0WYCSVDA2sOoWFyw2tCFulMsYEsSNYse/xUzO//8NZSIdwt5yWNQTkiYcNIPd4BuXnCLIFltykYanqYB+2sufG2LNhmg/qoR6L4tgwR/YNvk26GlgB1zwLiV+NVOgJu3HN/FRtgTtV++BcjPpFE4l0ypzY0KVGzuYVaAlOA7vRLxKXPZ0yeGkoLq0pfg1nrfnzQsxteAlivhLVU1NqpJHv2Sp/7GxBrBcbNAozwC1hZQHtYzfxvEp9w0NGCS4HREBdLX4B5RoKoLwMh2DcYYH7T+ql25zeFczhtonOZ8EM4ayAJbVeMbOwAO7MWWP6D3R2Ev5Z4KwxPGcd2hzz4V7H3TCro++VAAPcIlHnID+BydATzZbkkNu1OJpXwcccwXwxR/S7fPYeqs0gbBWj4qrVbrmtpOJlvdnM3ll93dq8liZI6avdPO0STpRbOHPyCVGFmVc59/RPxuTfU/s5VZKir7FWTyetNZoel+Y+bYaeXmthMZ/3fSTjCatDvm5y3DoJQ== saint@keisen.local

Privatus arba slaptas raktas:

❯ head /tmp/naujasraktas -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDo6hOLoH

❯ tail /tmp/naujasraktas GbyBQQV4acgQcB/TwXL0iod4PqpdgK9+KzXzVjdI6kr4Z3VdFvTdZ1tTILuo48I8Pb0q2Z qcDgWXj6lvg3y1WFy9/QN/w3E= -----END OPENSSH PRIVATE KEY-----

Tai kuo jie skirias? Kadangi dauguma pradedančiųjų adminų turi dėmesio sutrikimo problemų, šį kartą parodau:

  • Viešas raktas turi failo plėtinį .pub
  • Viešas raktas prasideda ssh-rsa
  • Privatus raktas prasideda ——-BEGIN OPENSSH PRIVATE KEY——-
  • Privatus raktas baigiasi ——-END OPENSSH PRIVATE KEY——-

Administratoriui reikia siųsti raktą, kuris yra viešas – ssh-rsa .pub.

Kompas Windows

Su windows kiek sudėtingiau – reikia daugiau machinacijų. Aš įtariu, kad dabar egzistuoja ir kitokie variantai, panašesnį į MacOS per cmd, bet kadangi esu senis su barzda, tai parodysiu old school metodą.

  1. Siunčiamės puttygen.exe
  2. Reikėtų patikrinti parašą, bet kas ten jį tikrina, gerai, jei darbiniam kompe yra antivirusas. O jei rimtai – parašysiu ir apie tai, kaip galima patikrinti tuos parašus.
  3. Pasileidžiam programą, pasirenkam RSA, rakto dydį 4096 ir generuojam. Atrodo va taip: putty key generator
  4. Išsaugom privatų raktą kur nors.
  5. Nusikopijuojam ssh-rsa dalį ir nusiunčiam adminui – ją galima ramiai siuntinėti per emailą ar slacką ar teamsus.
  6. Galima išsaugoti public dalį ir putty formatu, bet jos geriau nesiųsti adminui. Nors ją galima transformuoti į OpenSSH formatą – sutaupysi admino laiko ir erzulį.

N.B. Viešo rakto galima nesaugoti, burtai (matematika) tokia gudri, kad iš privataus rakto galima pagaminti viešą bet kada.

Pvz kaip atrodo ppk viešas raktas ne OpenSSH formatu:

❯ cat ZmonosViesasRaktas.ppk ---- BEGIN SSH2 PUBLIC KEY ---- Comment: "rsa-key" AAAAB3NzaC1yc2EAAAADAQABAAACAQCU4Oj4XH2lzuDUic9oTo24BsebV4RT1cQI QTwDhNS/i3npLN4u80ZRhR7m0j7UdZRDSlDcUovtSvdYAO2NPiwVPJGMLZWeAS/N YMGwdRV9CGtfhpTRXJEVhkbv2+gG0pIHA0XtOlkxX9VMKCUGdT2tmtpZgyaKIwNj bMuJU3GJATfAi4yG0Q6hTiqaLDFf2IYnk4MizT9WRWpbe9BvH0JNXGRjGKbl2ez5 ichdw5EzPt1UVInORJTMvO8o3xytCB7qqCEVjwO/e/Xcfk4OnrXAynfhqCsPEyg1 MhU9QlelEY6Q0HsUcDHesJuBATPs6UWniL9EaIq72EbrDhpGQlsV5lPWMceV0F6E mQ38MPQjay+48C+LHtIgTiq1woMkOoHoMtnRubvQ8JW916OLX60iafAYbO9LY/Jz sQ9j1BJ7zT0SnwdE8dPcTH+Q4hDU00729J6Fxuhod+8BYhdLofN47xiyPZ5zyxW5 l4yvYb8eyrzmNYok2epBOXGb7WEB9zjxmupeD0iJMHCkfONS3ltNDa49OLgHLEeM NakQ4i5blspublh9v3/3hpLqD3+CQLalV0rNDy91m9oqK9oTX7K6RXlCbXNvDNY6 SNIL64CRmQvcOKQk8/8aboANzBJfgHxbaYe75bYsOy4R6kyTy2/l6UZomlJi3eTu OoUMXqnKdw== ---- END SSH2 PUBLIC KEY ----

OpenSSH – tai dažniausiai naudojama programa-serveris, per kurią jungiamasi prie serverio.

Tai štai – raktą išsiuntėm, laukiam dabar admino atsakymo, greičiausiai reikės ir firewall sutvarkyt (atidaryt prieigą), dar kokio nors moralo išklausyt ir bus galima prisijungti.

 
Read more...

from Ką daryt?

Taigi, stuktelėjus 42 metams, nori nenori pradedi susimąstyti apie reinkarnaciją ir kas bus su projektais, kurie paleisti ir prižiūrimi, o gal net jais kas nors naudojasi. Nutariau pasirūpinti jų tęstinumu, bet Žmonai, aišku, šitas mano sumanymas taip pusiau patiko ;) Na, bet kur ji dėsis.. Makaronus tai verdu aš.

Taigi planas:

  • Inventorizuot sistemas ir surinkti informaciją apie jas į vieną vietą
  • Sugalvot ką daryt su slaptažodžiais
  • Išmokyt prižiūrėt serverius
  • Varyt į Tibetą

Inventoriziją ir slaptažodžių pasidalinimo techniką dar reikia sugalvot ir padaryt, o trumpas pamokėles, kaip reikėtų perimti serverius ir darbus iš sysadmino aprašysiu. Jos bus pritaikytos konkrečiam naudojimui, tai serveriai yra Linux amd64, o kai kurie – arm64, o administratoriaus darbo vieta – Windows (berods 11).

Kitas postas bus apie tai, kaip prisijungti prie serverių, kai senasis administratorius geras ir sako – “atsiųsk raktą”.

 
Read more...

from taiva

Sako man – reikia servisą naujint, bet taip naujint, kad nebūtų prastovų (downtime). Pirmiausia reikia žinot, kad garantijų, kad niekada nieko nesugadinsi – nėra, tad 0% downtime (arba 100% uptime) nebūna – net ir visada anksčiau ar vėliau pasibaigs ir dar neaišku ar su restart nuo 0 ar kaip ten kitaip. Internetuose informacijos ir melo, kad galima vis tik taip padaryt pilna, bet angliškai. O ne visi anglų kalbą moka, o būna, kad ir moka, bet skaito ir nieko nesupranta. Tad čia pusiau versta, pusiau iš atminties, bet įkvėpus dūmo taiva taip gaunas:

  • Padaryti taip, kad būtų profilaktinės valandos/dienos/savaitgaliai ir neskaičiuoti to laiko, kaip prastovos. Nesąmonė, rėkia programeris, taip negalima! Galima – šitas būdas labai dažnai naudojamas bankų. Jie sako – savaitgalį atsiskaitymas kortelėmis neveiks ir ką tu jiems, špyga taukuota. Garantuotai ten šitas neveikimas neįtraukiamas į probleminį ir ten naujina tą servisą naujina, kol ateina pirmadienis ir kažkas sutvarko viską ir paleidžia veikti. Ne kartą taip buvo ir ne kartą taip bus.
  • Mėlynai/Žali atnaujinimai. Arba kitaip – blue/green. Kai išleidžiama nauja versija – ji paleidžiama green serveriuose, virtualiose mašinose ar tiesiog zonoje, o vartotojų ar kitoks kreipinių srautas palaipsniui nukreipimas, kad pažaliuotų. Mėlyna (senoji) versija aptarnauja vis mažiau ir mažiau užklausų, kol galų gale lieka tik kaip atsarginis variantas (ale atsukti atgal, jei šūdas gavos), išjungiama arba gali tarnauti kaip vieta būsimai žaliai zonai. Beje, tai gali būti ir vienas serveris, kuris teikia paslaugą iš skirtingų direktorijų. Aišku, vienas serveris gali bet kada nulūžti ir paslauga sustos, bet deploymentas tai visvien bus zero downtime :)
  • Uždususios kanarėlės strategija (cannary) – apie pavadinimą galima sužinoti čia. Panašiu principu veikia ir pats serviso naujinimas – nauja versija paleidžiama serveriuose, į kuriuos srautas kreipiamas po truputį ir stebima situacija: žurnalai (logs), metrikos, galbūt vartotojų nusiskundimai; nustatoma kažkokia tolerancijos riba (klaidų retai kada pavyksta išvengti) ir neskubant vis daugiau vartotojų naudoja naują versiją. Jei pasiekta klaidų riba ar koks kitoks kriterijus – galima lengvai srautą kreipti atgal į seną versiją ir sutvarkyt problemas. Dažniausiai kanarėles naudoja daug vartotojų turinčios įmonės, kur pakeitimai gali iššaukti sniego lavinos efektą. Dar vienas privalumas – kad lengva ištestuoti fyčerius – t.y. pasinaudoja ne tik technologai, bet ir marketingistai. Turbūt teko laukti kokios nors naujienos, kuri paleista Amerikėj, bet nėra dar Lietuvoj – tai va, stebi ar kanarėlės dūsta ar ne.

Na, bet dar reikia visą tai automatizuoti, nes reikėtų, kad klaidas darytų kompiuteris vienodas, o ne žmonės skirtingas. Apie tai – gal kitą kartą.

 
Read more...

from saint

Caddyfile for running Lemmy

How to follow Lemmy community from Mastodon

Spent a couple of hours.. wanted to follow a Lemmy community from group.lt instance on Mastodon. Here is a working config for Caddy (Caddyfile):

group.lt {
	reverse_proxy	http://lemmy_lemmy-ui_1:1234
        tls saint@ghost.lt {
}

@lemmy {
        path    /api/*
        path    /pictrs/*
        path    /feeds/*
        path    /nodeinfo/*
        path    /.well-known/*
}

@lemmy-hdr {
	header Accept application/*
}

@lemmy-post {
	method POST
}

handle @lemmy {
        reverse_proxy   http://lemmy_lemmy_1:8536
}

handle @lemmy-hdr {
        reverse_proxy   http://lemmy_lemmy_1:8536
}

handle @lemmy-post {
        reverse_proxy   http://lemmy_lemmy_1:8536
}

The key point here was

@lemmy-hdr {
	header Accept application/*
}

I have taken a hint from lemmy.coupou.fr

and from some nginx conf for lemmy

 
Read more...

from saint

Reclaiming space in synapse postgresql database

Follow the fat elephant

I have received an alert from Grafana – that my synapse directory is almost full, which was kinda strange as I have given 100GB partition to it just a couple of weeks ago.. So I have put a hat, picked up some cider and something to smoke and went to the adventure.

From the old times I knew that postgresql database size can be reduced using vacuumdb. Entered the container and boom – after 15 or so minutes it has finished and reclaimed 100MB of space.. Hmmm... Interesting – which table eats the space. Google, link

SELECT
    relname AS "relation",
    pg_size_pretty (
        pg_total_relation_size (C .oid)
    ) AS "total_size"
FROM
    pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C .relnamespace)
WHERE
    nspname NOT IN (
        'pg_catalog',
        'information_schema'
    )
AND C .relkind <> 'i'
AND nspname !~ '^pg_toast'
ORDER BY
    pg_total_relation_size (C .oid) DESC
LIMIT 5;
 relation      | total_size
--------------------+------------
 state_groups_state | 65 GB
 event_json         | 1197 MB
 event_edges        | 619 MB
 events             | 595 MB
 event_auth         | 528 MB

Alright!!! Google: stategroupsstate, link and found a compression tool.

git clone, crap a short docker-compose.yml and build the tool.

root@instance-20211112-2005:/opt/synapse-compress-state# cat docker-compose.yaml
---
version: "3.5"
services:
  synapse-compress:
      build:
        context: rust-synapse-compress-state/
      command: synapse_auto_compressor -p postgresql://user:pass@dbhost/dbname -c 500 -n 100
      networks:
          - synapse

networks:
        synapse:
                name: synapse

let's crap some more:

root@instance-20211112-2005:/opt/synapse# cat /opt/synapse-compress-state/run.sh
#!/bin/bash

cd /opt/synapse-compress-state/

docker-compose up

put it into crontab:

@daily /opt/synapse-compress-state/run.sh > /dev/null

later googled more and found some smarter people than me: shrink synapse database and that really helped, especially reindexing.

 
Read more...

from saint

How to send mail using php mail() function in docker way

Using ssmtp to send email to the relay container

Sometimes we want to run dockerized old php site that we do not want to work with, or a programmer is gone and nobody cares to make changes to use email relay host, such as mailgun or gmail or anything else. In the Linux VM or bare metal server it is quite easy task – you run web server and mail server two in one and mail server takes care of mail routing.

In the dockerized environment usually you want to run the least amount of services possible in the container, so sending mail using PHP's mail() function becomes a tricky.

Let's create a docker-compose.yml file, containing all required containers:

caddy – web server

php – php server for the app – the trick here to use msmtp as a sendmail to have mail sent to remote server (our mail container)

mail – smtp relay server, we will use postfix

the source code is here: docker-php-mail-example

the main thing is to use ssmtp on the php container and send data to mail container

enjoy!

 
Read more...