Introduction
A VPS isn’t truly “alive” until it can send emails. System alert notifications, SSL certificate expiration reminders, backup failure reports, user registration verifications — all require email delivery capability.
Services like SendGrid, Mailgun, and Resend exist, but they either have tight limits or require a credit card before you can use them. The Gmail API is completely free: every Google account can send up to 100 emails per day, which is more than enough for personal VPS operations.
This guide walks you through setting up Gmail API email sending from scratch, supporting both Python and Shell scripts, so you can ditch third-party email service bills for good.
1. Why Choose Gmail API?
| Solution | Cost | Daily Limit | Setup Complexity |
|---|---|---|---|
| Gmail API | Free | 100/day | ⭐⭐ |
| SendGrid Free | Free | 100/day | ⭐⭐ |
| Mailgun Free | Free | 100/day | ⭐⭐⭐ |
| Self-hosted SMTP | Free | Unlimited | ⭐⭐⭐⭐⭐ |
| Amazon SES | Pay-as-you-go | 200/day (Sandbox) | ⭐⭐⭐ |
Gmail API advantages:
- Zero cost: No credit card required — just use your Google account
- High deliverability: Gmail’s IP reputation keeps your emails out of spam folders
- Built-in labeling: Emails automatically categorized in Gmail for easy management
- Zero maintenance: Google handles the infrastructure — you just send
⚠️ Note: If you need to send bulk marketing emails (like newsletters), Gmail API is not the right choice — don’t use a personal Gmail account for mass sending. This guide covers operational notifications and transactional emails.
2. Prerequisites: Setting Up a Google Cloud Project
2.1 Create a Project
- Open Google Cloud Console
- Click the project dropdown at the top → New Project
- Enter project name:
VPS Email Sender(any name works) - Wait for creation and ensure the project is selected
2.2 Enable the Gmail API
- Go to APIs & Services → Library
- Search for Gmail API
- Click it → Enable
2.3 Configure OAuth Consent Screen
- Go to APIs & Services → OAuth consent screen
- Choose External → Create
- Fill in:
- App name: VPS Email Sender
- User support email: Your email
- Developer contact: Your email
- Skip the Scopes step (click “Save and Continue”)
- Test users: Add your Gmail address
- Click Back to Dashboard → Publish App
2.4 Create Credentials
- Go to APIs & Services → Credentials
- Click Create Credentials → OAuth client ID
- Application type: Desktop application
- Name:
VPS Email Client - After creation, click Download JSON and save as
credentials.json
3. Install Dependencies & Send Your First Email
3.1 Server Setup
Install Python dependencies on your VPS:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
3.2 Authorization Script
Create an authorization script, run it once on your local machine, then transfer the token to your VPS:
# quickstart.py
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
def authenticate():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.json', 'w') as token:
token.write(creds.to_json())
return creds
if __name__ == '__main__':
authenticate()
print("✅ Authorization successful! token.json generated")
Run this on your local machine (it needs a browser):
python3 quickstart.py
A browser window will open asking you to log in to Google. After authorization, token.json will be generated.
💡 Pro tip: If your VPS has no GUI, run the auth locally, then copy
token.jsonandcredentials.jsonto your VPS usingscp.
3.3 Send Email Script
After uploading token.json to your VPS, use this script:
# send_email.py
import base64
import os
import sys
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from email.mime.text import MIMEText
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
def get_service():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
raise Exception("Token invalid, please re-authorize")
return build('gmail', 'v1', credentials=creds)
def send_email(to, subject, body):
service = get_service()
message = MIMEText(body, 'plain', 'utf-8')
message['To'] = to
message['Subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
try:
result = service.users().messages().send(
userId='me', body={'raw': raw}
).execute()
print(f"✅ Sent! Message ID: {result['id']}")
return True
except HttpError as error:
print(f"❌ Failed: {error}")
return False
if __name__ == '__main__':
to = sys.argv[1] if len(sys.argv) > 1 else input("To: ")
subject = sys.argv[2] if len(sys.argv) > 2 else "Test Email"
body = sys.argv[3] if len(sys.argv) > 3 else "This is a test email sent via Gmail API."
send_email(to, subject, body)
Test it:
python3 send_email.py "your-email@gmail.com" "Gmail API Test" "Hello from my VPS! 🎉"
4. Advanced: Shell Script Wrapper
For system scripts and crontab tasks, create a simple shell wrapper:
#!/bin/bash
# send_email.sh — Send email via Gmail API
# Usage: ./send_email.sh "recipient" "subject" "body"
TO="$1"
SUBJECT="$2"
BODY="$3"
python3 -c "
import sys
sys.path.insert(0, '/opt/email-sender')
from send_email import send_email
send_email('$TO', '$SUBJECT', '$BODY')
"
Add it to crontab for system monitoring:
# Check SSL certificate daily at 8 AM
0 8 * * * /opt/scripts/check_ssl.sh || /opt/email-sender/send_email.sh "admin@example.com" "⚠️ SSL Certificate Expiring Soon" "$(date): Certificate check failed, please investigate"
# Monthly VPS summary on the 1st
0 9 1 * * /opt/scripts/summary.sh && /opt/email-sender/send_email.sh "admin@example.com" "📊 Monthly VPS Report" "$(cat /tmp/monthly_report.txt)"
5. Practical Examples: System Monitoring Integration
5.1 Disk Space Alert
#!/bin/bash
# disk_alert.sh
THRESHOLD=85
USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE" -ge "$THRESHOLD" ]; then
/opt/email-sender/send_email.sh \
"admin@example.com" \
"🚨 Disk Space Alert: ${USAGE}% Used" \
"Server: $(hostname)\nTime: $(date)\nDisk Usage: ${USAGE}%\n\n$(df -h)"
fi
Add to crontab for hourly checks:
0 * * * * /opt/scripts/disk_alert.sh
5.2 Docker Container Restart Notification
#!/bin/bash
# docker_restart_notify.sh
RESTARTED=$(docker ps -a --filter "status=exited" --format "{{.Names}} {{.Status}}" | head -5)
if [ -n "$RESTARTED" ]; then
/opt/email-sender/send_email.sh \
"admin@example.com" \
"🔄 Docker Container Crashed" \
"These containers have exited:\n\n$RESTARTED\n\nPlease investigate."
fi
5.3 Nginx Error Log Monitor
#!/bin/bash
# nginx_error_monitor.sh
ERRORS=$(tail -n 50 /var/log/nginx/error.log | grep -iE "error|critical|alert" | tail -10)
if [ -n "$ERRORS" ]; then
/opt/email-sender/send_email.sh \
"admin@example.com" \
"⚠️ Nginx Errors Detected" \
"Recent Nginx error log entries:\n\n$ERRORS"
fi
6. Security & Optimization
6.1 Token Management
token.jsonis the key to your email — never commit it to GitHub- Recommended path:
/opt/email-sender/token.json - Set permissions:
chmod 600 /opt/email-sender/token.json - Use
git-cryptorsopsfor encrypted storage
6.2 Rate Limiting
Gmail API quotas:
- 100 messages per day (free account)
- Maximum 10 messages per 100 seconds
- Exceeding limits returns
429 Too Many Requests
Add retry logic to your script:
import time
from googleapiclient.errors import HttpError
def send_with_retry(service, body, max_retries=3):
for attempt in range(max_retries):
try:
return service.users().messages().send(
userId='me', body=body
).execute()
except HttpError as e:
if e.resp.status == 429 and attempt < max_retries - 1:
wait = 2 ** attempt
print(f"⚠️ Rate limited, waiting {wait}s...")
time.sleep(wait)
else:
raise
return None
6.3 Multi-Account Rotation
If you need more than 100 emails/day, prepare multiple Gmail accounts and rotate:
TOKENS = ['token1.json', 'token2.json', 'token3.json']
current = 0
def get_next_token():
global current
token = TOKENS[current % len(TOKENS)]
current += 1
return token
7. Troubleshooting
❌ Token has been expired or revoked
Cause: Token expired or was revoked
Fix: Delete old token.json and re-run authorization
❌ Access blocked: XXXXXXXXX
Cause: Google security check triggered Fix: Ensure OAuth app is published (set to “Production”) and test user is added
❌ Recipient address rejected
Cause: Invalid or non-existent recipient address Fix: Verify the recipient email is correct
❌ Email goes to Gmail Spam
Cause: IP reputation or content flagged Fix:
- Add SPF/DKIM records (if sending from a custom domain)
- Avoid excessive URLs
- Don’t use trigger words (“urgent”, “free”, etc.)
Summary
Using the Gmail API to send emails is one of the most cost-effective and reliable solutions for VPS operations. The entire setup requires zero cost and about 15 minutes of configuration — once done, your server’s monitoring and notification system is fully operational.
Key takeaways:
- ✅ Free: $0 cost, 100 emails/day is plenty for personal operations
- ✅ Efficient: Gmail’s deliverability far exceeds self-hosted mail servers
- ✅ Simple: One Python script + one token file
- ✅ Scalable: Multi-account rotation, HTML templates, custom labeling
Start today — ditch paid email services and give your VPS its own email sending capability!
This article is part of the “Cloud Savings” series. Follow selfvps.net for more self-hosting tips.
