13 KiB
Email Signature Manager
A self-hosted, Dockerized Google Workspace email signature manager. Runs as a single container — designed for Unraid but works on any Docker host.
Features
- Pulls user data automatically from Google Workspace Directory API
- Renders per-user HTML signatures via Handlebars templates
- Pushes signatures directly to Gmail via
sendAsAPI (web + mobile) - Nightly batch push via configurable cron schedule
- Web admin UI — live template editor with real-time preview
- Single-user push for testing and onboarding
- SQLite audit log of every push event
- Basic auth protected UI
- Zero external services — single container, no database server
Environment Variables
All secrets and configuration are passed as environment variables at runtime.
No .env file is committed to this repo — see .env.example for all variable names and descriptions.
| Variable | Description | Default |
|---|---|---|
GOOGLE_ADMIN_EMAIL |
Workspace admin email for Directory API | (required) |
GOOGLE_CUSTOMER_ID |
Use my_customer for primary domain |
my_customer |
SERVICE_ACCOUNT_KEY_PATH |
Path to sa.json inside container | /app/secrets/sa.json |
ADMIN_USERNAME |
Web UI login username | (required) |
ADMIN_PASSWORD |
Web UI login password | (required) |
PORT |
Internal app port | 3000 |
CRON_SCHEDULE |
Cron expression for nightly push | 0 2 * * * |
NODE_ENV |
Node environment | production |
On Unraid
Set each variable directly in the Docker container template UI under the "Variables" section. No file needed on the host.
For Local Development
Copy .env.example to .env, fill in your values, then run:
docker-compose --env-file .env up -d --build
Part 1 — Google Cloud Console Setup
This section walks through creating the service account and downloading the key that allows the app to impersonate users in your Google Workspace domain.
Step 1 — Create a Google Cloud Project
- Open https://console.cloud.google.com and sign in with your Google Workspace admin account
- Click the project dropdown in the top navigation bar (next to the Google Cloud logo)
- Click New Project in the top right of the dialog
- Enter a project name:
email-sig-manager - Leave the organization as your Workspace domain → click Create
- Wait a few seconds, then select the new project from the dropdown to make it active
Note: If your org already has a GCP project you prefer to use, you can skip project creation and just enable the APIs inside the existing project.
Step 2 — Enable Required APIs
The app needs two Google APIs enabled in your project.
Enable Admin SDK API:
- In the GCP Console, go to APIs & Services → Library (left sidebar)
- Search for
Admin SDK API - Click the result → click Enable
Enable Gmail API:
- Go back to APIs & Services → Library
- Search for
Gmail API - Click the result → click Enable
Both should now show as Enabled under APIs & Services → Enabled APIs & Services.
Step 3 — Create the Service Account
- In the left sidebar, go to IAM & Admin → Service Accounts
- Click + Create Service Account at the top
- Fill in the details:
- Service account name:
email-sig-manager - Service account ID: will auto-fill as
email-sig-manager - Description:
Email signature push service for Google Workspace
- Service account name:
- Click Create and Continue
- On the "Grant this service account access to project" step — skip this, click Continue
- On the "Grant users access" step — skip this too, click Done
You will land back on the service accounts list and see your new account listed.
Step 4 — Enable Domain-Wide Delegation on the Service Account
- Click the service account name (
email-sig-manager) in the list to open it - Click the Edit button (pencil icon) at the top
- Scroll down to find "Show advanced settings" — click it to expand
- Check the box for "Enable Google Workspace Domain-wide Delegation"
- Enter a product name for the consent screen if prompted (e.g.,
Email Signature Manager) - Click Save
After saving, return to the service account detail page. You should now see a
"Domain-wide delegation" line showing a Client ID (a long numeric string,
e.g., 112233445566778899). Copy this Client ID — you will need it in Part 2.
Step 5 — Download the Service Account JSON Key
- On the service account detail page, click the Keys tab
- Click Add Key → Create New Key
- Select JSON as the key type → click Create
- The key file downloads automatically to your computer
- Rename the file to
sa.json - Store it securely — this file grants impersonation access to all users in your domain
This file goes in the
secrets/folder on your Unraid host. It is gitignored and must never be committed to GitHub.
Part 2 — Google Admin Console Authorization
This section delegates authority to the service account inside Google Admin, allowing it to impersonate users and update their Gmail signatures.
Step 6 — Authorize the Service Account in Google Admin
- Open a new tab and go to https://admin.google.com
- Sign in with your Google Workspace super admin account
- In the left sidebar, navigate to: Security → Access and data control → API controls
- On the API controls page, click Manage Domain Wide Delegation
- Click Add new to add a new authorized client
Fill in the form:
- Client ID: Paste the numeric Client ID you copied in Step 4 (find it again at GCP Console → IAM & Admin → Service Accounts → your account → Details)
- OAuth Scopes: Paste the following exactly, as one comma-separated line:
https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/gmail.settings.basic
- Click Authorize
If you receive an error saying "client ID not found", wait 2–3 minutes after enabling domain-wide delegation in Step 4 and try again — GCP propagation can take a moment.
Step 7 — Verify the Authorization
- After clicking Authorize you should see the new entry in the list
- Click View details next to your entry
- Confirm both scopes are listed:
https://www.googleapis.com/auth/admin.directory.user.readonlyhttps://www.googleapis.com/auth/gmail.settings.basic
- If a scope is missing, click Edit, re-enter the full comma-separated scope string, and click Authorize again
Multi-party approval note: If your Google Workspace org has multi-party approval enabled for admin actions, another super admin will need to approve this delegation before it takes effect.
Part 3 — Unraid Deployment
Step 8 — Clone the Repository
SSH into your Unraid server or use the Unraid terminal:
cd /mnt/user/appdata
git clone https://github.com/YOURUSERNAME/email-sig-manager.git
cd email-sig-manager
Step 9 — Place the Service Account Key
Copy sa.json (downloaded in Step 5) into the secrets/ folder:
# From your PC to Unraid (run this on your PC):
scp sa.json root@UNRAID-IP:/mnt/user/appdata/email-sig-manager/secrets/sa.json
Or copy it via your Unraid SMB share using Windows Explorer:
\\UNRAID-IP\appdata\email-sig-manager\secrets\
Step 10 — Add Your Company Logo
Place your logo file at:
/mnt/user/appdata/email-sig-manager/public/assets/logo.png
Recommended: PNG, transparent background, 160×160px or smaller.
Step 11 — Configure Container in Unraid Docker UI
- In the Unraid web UI, go to Docker → Add Container
- Set the following:
- Name:
email-sig-manager - Repository: (leave blank if building locally — use the path method below)
- Network Type: Bridge
- Name:
Add these Volume mappings:
| Container Path | Host Path |
|---|---|
/app/secrets |
/mnt/user/appdata/email-sig-manager/secrets |
/app/data |
/mnt/user/appdata/email-sig-manager/data |
/app/public/assets |
/mnt/user/appdata/email-sig-manager/public/assets |
Add these Port mapping:
| Container Port | Host Port |
|---|---|
3000 |
3000 (or any open port) |
Add these Variables (click Add Variable for each):
| Name | Value |
|---|---|
GOOGLE_ADMIN_EMAIL |
admin@messagepointmedia.com |
GOOGLE_CUSTOMER_ID |
my_customer |
SERVICE_ACCOUNT_KEY_PATH |
/app/secrets/sa.json |
ADMIN_USERNAME |
admin |
ADMIN_PASSWORD |
(your chosen password) |
PORT |
3000 |
CRON_SCHEDULE |
0 2 * * * |
NODE_ENV |
production |
Step 12 — Build and Start
cd /mnt/user/appdata/email-sig-manager
docker-compose up -d --build
Part 4 — First Run & Verification
Step 13 — Access the Admin UI
Open a browser and navigate to:
http://UNRAID-IP:3000
Log in with your ADMIN_USERNAME and ADMIN_PASSWORD.
Step 14 — Test with a Single User First
- Go to the Template Editor page
- Verify the live preview renders correctly with your info
- Make any template adjustments needed
- Return to the Dashboard
- In the Push Single User field, enter your own email address
- Click Push Single User
- Open Gmail in a new tab → compose a new email → verify the signature appears correctly
- Check the Gmail mobile app as well (force-close and reopen if needed)
Step 15 — Push to All Users
Once you've verified your own signature looks correct:
- Click Push to All Users on the Dashboard
- Confirm the dialog
- Watch the audit log populate with success/error statuses
- The nightly cron will take over automatically from here
Updating the Project
cd /mnt/user/appdata/email-sig-manager
git pull
docker-compose up -d --build
secrets/, data/, and host-managed files are gitignored and unaffected by pulls.
Project Structure
email-sig-manager/
├── src/
│ ├── index.js # Express app entry
│ ├── routes/
│ │ ├── admin.js # Template, logs, users API
│ │ └── push.js # Signature push logic + batch runner
│ ├── services/
│ │ ├── googleAdmin.js # Directory API — fetch all users
│ │ ├── gmailApi.js # Gmail sendAs patch
│ │ ├── renderer.js # Handlebars template renderer
│ │ └── scheduler.js # node-cron nightly job
│ └── db/
│ ├── sqlite.js # DB init and connection
│ └── audit.js # Audit log read/write
├── templates/
│ └── default.hbs # 2-column HTML signature template
├── public/
│ ├── dashboard.html # Admin dashboard UI
│ ├── editor.html # Template editor with live preview
│ └── assets/
│ └── logo.png # Company logo (add your own)
├── secrets/ # Gitignored — sa.json goes here
├── data/ # Gitignored — SQLite DB lives here
├── Dockerfile
├── docker-compose.yml
├── package.json
└── .env.example # Variable reference — safe to commit
Cron Schedule Reference
| Expression | Meaning |
|---|---|
0 2 * * * |
2:00 AM every day (default) |
0 6 * * * |
6:00 AM every day |
0 2 * * 1 |
2:00 AM every Monday |
0 2 1 * * |
2:00 AM on the 1st of each month |
Troubleshooting
Container won't start
Run docker logs email-sig-manager. Most likely a missing env variable or malformed sa.json.
"No primary sendAs" error The user may not have an active Gmail account, or domain-wide delegation scopes were not saved correctly in Google Admin.
"Client ID not found" when authorizing in Google Admin Wait 2–3 minutes after enabling domain-wide delegation in GCP and try again.
Signatures not showing on mobile Gmail iOS/Android automatically uses the web signature. Have the user force-close and reopen the Gmail app after the push.
Template changes not saving
Verify the container has write access to the templates/ directory. A .bak file is created on every save.
401 / permission denied errors in logs The OAuth scopes in Google Admin may not have saved correctly. Go back to Admin Console → Security → API Controls → Domain Wide Delegation → View Details and verify both scopes are listed.
Security Notes
sa.jsongrants impersonation rights to every user in your domain — treat it like a master key- Never commit
sa.jsonor a populated.envto GitHub - Change
ADMIN_PASSWORDbefore first deployment - Consider placing the UI behind HTTPS if accessible outside your LAN