Code, Explained

How to Build a Docker SMTP Relay on Ubuntu Using Postfix

If your applications need to send emails reliably, an SMTP relay is one of the cleanest solutions.

In this tutorial, we will build a lightweight SMTP relay using Docker and Postfix on Ubuntu. Your applications will send email locally to the relay, and the relay will securely forward mail through providers like Amazon SES, SendGrid, Mailgun, or Gmail SMTP.

This setup is ideal for:

  • Laravel applications
  • WordPress websites
  • Node.js apps
  • Dockerized services
  • Internal notification systems
  • Transactional emails

Architecture

Application
    ↓ SMTP
Docker Postfix Relay
    ↓ TLS SMTP
Amazon SES / SendGrid / Mailgun
    ↓
Recipient Inbox

Prerequisites

Before starting, make sure you have:

  • Ubuntu server
  • Docker installed
  • Docker Compose plugin installed
  • SMTP provider credentials

Supported providers include:

  • Amazon SES
  • SendGrid
  • Mailgun
  • Gmail SMTP
  • Postmark

Step 1 — Install Docker

Update Ubuntu:

sudo apt update

Install Docker:

sudo apt install -y docker.io docker-compose-plugin

Enable Docker:

sudo systemctl enable --now docker

Verify installation:

docker --version

Optional: run Docker without sudo

sudo usermod -aG docker $USER
newgrp docker

Step 2 — Create Project Directory

Create a working directory:

sudo mkdir -p /opt/smtp-relay
cd /opt/smtp-relay

Step 3 — Create Persistent Storage

Create directories for mail queue and logs:

sudo mkdir -p relay
sudo mkdir -p logs

These directories ensure queued emails survive container restarts.


Step 4 — Create Docker Compose File

Create a docker-compose.yml file:


services:
  smtp-relay:
    image: boky/postfix
    container_name: smtp-relay
    restart: unless-stopped

    ports:
      - "25:25"

    environment:
      # Upstream SMTP provider
      RELAYHOST: smtp.gmail.com
      RELAYHOST_PORT: 587
      RELAYHOST_USERNAME: YourSMTPEnabledGmailUserID
      RELAYHOST_PASSWORD: YourGmailPassword

      # Allowed sender domains
      ALLOWED_SENDER_DOMAINS: wempro.com,pumpsandinstrumentations.com

      # Relay hostname
      POSTFIX_myhostname: relay.vmi3202307.local
      POSTFIX_mynetworks: 127.0.0.0/8 172.16.0.0/12 192.168.0.0/16

      POSTFIX_smtpd_recipient_restrictions: permit_mynetworks,reject_unauth_destination

      TZ: UTC

    volumes:
      # Mail queue persistence
      - ./relay:/var/spool/postfix

      # Optional logs
      - ./logs:/var/log

    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"



Save the file.


Step 5 — Start the SMTP Relay

Launch the container:

docker compose up -d

Verify container status:

docker ps

View logs:

docker logs -f smtp-relay

Step 6 — Test Email Sending

Install swaks:

sudo apt install -y swaks

Send a test email:

swaks \
  --to you@example.com \
  --from noreply@yourdomain.com \
  --server localhost:25 \
  --header "Subject: SMTP Relay Test" \
  --body "SMTP relay is working"

Successful output:

250 2.0.0 Ok: queued as ...

Step 7 — Configure Your Application

Your applications should connect to:

localhost:25

Example DSN:

smtp://localhost:25

Laravel .env example:

MAIL_MAILER=smtp
MAIL_HOST=localhost
MAIL_PORT=25
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="Your App"

Multiple Domain Support

To allow multiple sender domains:

ALLOWED_SENDER_DOMAINS: domain1.com,domain2.com,domain3.com

Why Use an SMTP Relay?

Benefits include:

  • centralized email handling
  • provider abstraction
  • email queueing
  • retry handling
  • cleaner application configuration
  • rate limiting
  • easier provider switching

Important Security Tips

Do NOT Create an Open Relay

Never use:

POSTFIX_mynetworks: 0.0.0.0/0

This will allow the internet to abuse your server for spam.


SPF, DKIM, and Deliverability

For production use, verify your domain with your SMTP provider and configure:

  • SPF
  • DKIM
  • DMARC

Without these, emails may land in spam folders.


Queue Management

View mail queue:

docker exec -it smtp-relay postqueue -p

Flush queue:

docker exec -it smtp-relay postqueue -f

Final Thoughts

A Dockerized SMTP relay is a lightweight and reliable solution for modern applications. By combining Postfix with providers like Amazon SES or SendGrid, you get:

  • reliable delivery
  • secure outbound SMTP
  • local application integration
  • retry and queue management
  • simplified infrastructure

This setup works especially well for Docker-based deployments and internal application stacks.

Happy emailing!

Posted in ubuntuTagged , , , , , ,

Jailed Ubuntu SFTP User

> Add a user as system user (which will prevent to create home directory) but without login capability

$ sudo adduser moderpatshala --system --shell /usr/sbin/nologin

>> That user need a password to login, you can skip it if you want to use public key authentication which is more secured than password login
$ sudo passwd moderpatshala
>> Now fix jail directory as root owned
$ sudo chown root:root /home/moderpatshala
>> Fix permission, chroot required relax file mode for root location like drwxr-xr-x
$ sudo chmod 755 /home/moderpatshala
>> Provide a writable directory under jailed directory for your sftp user
$ sudo chown -R moderpatshala /home/moderpatshala/public_html

>> Now you need to change SSH demon settings. You can add (if your sshd configuration settings allowed) a different file which I prefer
$ sudo vi /etc/ssh/sshd_config
————- or ———————-
$ sudo vi /etc/ssh/sshd_config.d/80-user-moderpatshala.conf

Match User moderpatshala
  PasswordAuthentication yes
  PubkeyAuthentication no
  ChrootDirectory /home/moderpatshala
  ForceCommand internal-sftp
  X11Forwarding no
  AllowTcpForwarding no

>> Finally it’s time to restart your sshd
$ sudo systemctl restart ssh

Posted in linux, ubuntuTagged , ,

add new hard disk into ubuntu more than 2TB size

lsblk

will list available device

parted /dev/sdd

considered device “sdd” from lsblk output

(parted) mklabel gpt
(parted) mkpart primary ext3 0 100%
(parted) print
(parted) quit

mkfs.ext3 /dev/sdd1

this command will format this hard disk into ext3 file system as we instruct by parted command

mkdir /home/data

mounting point directory creating

mount -t ext3 /dev/sdd1 /home/data

mounting formatted hard disk into target directory

blkid

try to find ID of new hard disk to write into fstab so that after restart our hard disk will mount automatically

sample output of blkid

/dev/sdd1: UUID="20e4b16b-4d4c-4053-b6f4-a2c103f2db2f" TYPE="ext3" PARTLABEL="primary" PARTUUID="33658d68-726f-4398-992f-2aaafebe17ff"

vi /etc/fstab

give an entry for our new hard disk at the end of this file

example entry

UUID=20e4b16b-4d4c-4053-b6f4-a2c103f2db2f /home/data ext3 nofail 0 0
Posted in linux, ubuntuTagged , , , ,

recursive change group and file mode in linux and detach active login session

change group

nohup sh -c “find /any/path/that/need/to/change/* -group mygroup -exec chgrp www-data {} \;” > /dev/null 2>&1 &

traditional way

nohup sh -c “chgrp -R www-data /any/path/that/need/to/change” > /dev/null 2>&1 &

change mode

nohup sh -c “find /any/path/that/need/to/change/* -perm u=rw,g=r,o=r -execdir chmod g+w {} \;” > /dev/null 2>&1 &

traditional way

nohup sh -c “chmod -R g+w /any/path/that/need/to/change” > /dev/null 2>&1 &

Posted in linux, ubuntuTagged , , , , , ,

Install php 7.2 ssh2 in ubuntu 16x

recently i need to install ssh2 connection through my php code to connect remote server by sftp for file transfer. i’m using php 7.2 and face a “sigment fault” issue for normal installation. i solved this issue by install required demon. hope it may help someone who face same issue.

first you need to install/upgrade some basic program

apt-get install gcc make autoconf libc-dev pkg-config

then install base library

apt-get install libssh2-1-dev

now install required php modules

apt-get install php7.2-dev php-pear

now install ssh by pecl (the most important part of installation)

— pecl channel-update pecl.php.net
pear config-show
— pear config-set php_ini /etc/php/7.2/apache2/php.ini
— pear config-set temp_dir /etc/php/temp/pear
pecl install ssh2-1.1.2

ignore commented line OR use if you understand by yourself

now you need to enable ssh2 extension into your php cli installation

echo “extension=ssh2.so” > /etc/php/7.2/mods-available/ssh2.ini
ln -s /etc/php/7.2/mods-available/ssh2.ini /etc/php/7.2/cli/conf.d/30-ssh2.ini

please check you php installation/configuration path. set priority on your own. i set here 30 without proper understanding 😀

now another important part is your PHP code. when we use ssh2 in fopen wraper, in other version of ssh2 connection we need to open a connection and we can use resource id. but with the above change you must use user, password, port i.e. full access information every time we need to connect to server. here is my sample code –

$fh = @fopen(‘ssh2.sftp://’ . $this->user.’:’.$this->pass.’@’.$this->ip.’:’.(intval($this->port)>0?intval($this->port):22) . $pRemoteLocation, $pMode);

all other code like directory creation or any other command execution may be same as before.

Posted in php, ubuntuTagged , , , , , , ,