Decap CMS OAuth Authentication - Single Site Setup
Step-by-Step Setup
Part 1: Create GitHub OAuth Application
Step 1.1: Navigate to GitHub Settings
- Log into GitHub
- Click your profile picture (top right)
- Go to Settings
- Scroll down in left sidebar
- Click Developer settings (near bottom)
- Click OAuth Apps
- Click New OAuth App button
Step 1.2: Fill Out OAuth App Form
You’ll see a form with several fields:
Application name:
My Site CMS
(Replace “My Site” with your actual site name, e.g., “Boeygembos CMS”)
Homepage URL:
https://www.mysite.com
(Your actual website URL - must include https://)
Application description: (Optional)
OAuth authentication for Decap CMS content management
Authorization callback URL:
https://oauth.mysite.com/callback
Important notes about the callback URL:
- This is where GitHub redirects after authentication
- Must be
https://(nothttp://) /callbackpath is important - don’t change it- Can use subdomain of your site OR your VPS domain
- Examples:
https://oauth.boeygembos.be/callback✅https://oauth.mysite.com/callback✅https://mysite.com/oauth/callback✅
Which domain to use for OAuth?
- Same domain as site:
oauth.mysite.com(cleaner, recommended) - VPS domain:
oauth.yourvps.com(works for multiple sites) - Either works - just be consistent!
Step 1.3: Register and Save Credentials
- Click Register application
- You’ll see the app overview page
Copy the Client ID:
- Displayed on screen (looks like:
Iv1.abcdef123456) - Save this somewhere secure
Generate Client Secret:
- Click Generate a new client secret button
- IMPORTANT: Copy the secret immediately!
- It’s only shown once - you can’t see it again
- Save it securely (looks like:
abc123def456...)
Keep these safe:
Client ID: Iv1.abcdef123456
Client Secret: abc123def456ghi789jkl012mno345pqr678
You’ll need both in the next steps.
Part 2: Prepare DNS
Step 2.1: Choose OAuth Domain
Decide where your OAuth provider will live:
Option A: Subdomain of your site (Recommended)
oauth.mysite.com
- Clean and organized
- Each site gets its own OAuth subdomain
Option B: Shared OAuth domain
oauth.yourvps.com
- Can be reused for multiple sites
- Slightly more complex setup later
For this guide, we’ll use: oauth.mysite.com
Step 2.2: Add DNS Record
Log into your DNS provider (Domain.com, Cloudflare, etc.)
Add A Record:
Type: A
Name: oauth (or "oauth.mysite.com" depending on interface)
Value: YOUR_VPS_IP_ADDRESS
TTL: 3600 (1 hour)
Example for Domain.com:
- Login → Domain Management
- Click your domain → DNS Records
- Add Advanced DNS Record
- Type:
A - Refers to: Select “Other Host” → type
oauth - Points to: Your VPS IP (e.g.,
123.45.67.89) - TTL: 3600
- Save
Step 2.3: Verify DNS
Wait 2-5 minutes, then verify:
# From your computer or VPS
dig oauth.mysite.com +short
Should return your VPS IP address.
Or use online tool: https://dnschecker.org/
Part 3: Deploy OAuth Provider on VPS
Step 3.1: Create Directory Structure
SSH into your VPS:
ssh root@YOUR_VPS_IP
Create directory:
mkdir -p /opt/docker/decap-oauth
cd /opt/docker/decap-oauth
Step 3.2: Create Node.js Server
Create the OAuth server file:
nano server.js
Paste this code:
const express = require('express');
const cors = require('cors');
const { AuthorizationCode } = require('simple-oauth2');
const app = express();
const port = process.env.PORT || 3000;
// CORS configuration
app.use(cors({
origin: true,
credentials: true
}));
// OAuth2 configuration
const client = new AuthorizationCode({
client: {
id: process.env.OAUTH_CLIENT_ID,
secret: process.env.OAUTH_CLIENT_SECRET,
},
auth: {
tokenHost: 'https://github.com',
tokenPath: '/login/oauth/access_token',
authorizePath: '/login/oauth/authorize',
},
});
// Redirect to GitHub OAuth
app.get('/auth', (req, res) => {
const authorizationUri = client.authorizeURL({
redirect_uri: `${process.env.BASE_URL}/callback`,
scope: 'repo,user',
state: req.query.state || '',
});
console.log('Auth request received, redirecting to GitHub');
res.redirect(authorizationUri);
});
// OAuth callback
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokenParams = {
code,
redirect_uri: `${process.env.BASE_URL}/callback`,
};
const accessToken = await client.getToken(tokenParams);
const token = accessToken.token.access_token;
// Get the origin from environment
const redirectOrigin = process.env.REDIRECT_URL;
console.log('Authentication successful, sending token to CMS');
// Send token back to CMS via postMessage
const script = `
<!DOCTYPE html>
<html>
<head>
<title>Authentication Successful</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.message {
text-align: center;
padding: 2rem;
background: rgba(255,255,255,0.1);
border-radius: 10px;
backdrop-filter: blur(10px);
}
.spinner {
border: 3px solid rgba(255,255,255,0.3);
border-top: 3px solid white;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="message">
<h1>✓ Authentication Successful</h1>
<div class="spinner"></div>
<p>Redirecting back to CMS...</p>
</div>
<script>
(function() {
function receiveMessage(e) {
console.log("receiveMessage %o", e);
window.opener.postMessage(
'authorization:github:success:${JSON.stringify({ token, provider: 'github' })}',
e.origin
);
}
window.addEventListener("message", receiveMessage, false);
window.opener.postMessage("authorizing:github", "${redirectOrigin}");
// Auto-close after 2 seconds
setTimeout(function() {
window.close();
}, 2000);
})();
</script>
</body>
</html>
`;
res.send(script);
} catch (error) {
console.error('Authentication failed:', error.message);
res.status(500).send(`
<!DOCTYPE html>
<html>
<head>
<title>Authentication Failed</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
}
.message {
text-align: center;
padding: 2rem;
background: rgba(255,255,255,0.1);
border-radius: 10px;
backdrop-filter: blur(10px);
}
.error {
background: rgba(0,0,0,0.2);
padding: 1rem;
border-radius: 5px;
margin-top: 1rem;
font-family: monospace;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="message">
<h1>✗ Authentication Failed</h1>
<p>There was an error during authentication.</p>
<div class="error">${error.message}</div>
<p style="margin-top: 1rem; font-size: 0.9em;">
You can close this window and try again.
</p>
</div>
</body>
</html>
`);
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
message: 'OAuth provider is running',
uptime: process.uptime()
});
});
// Root endpoint
app.get('/', (req, res) => {
res.json({
message: 'OAuth Provider for Decap CMS',
endpoints: {
auth: '/auth',
callback: '/callback',
health: '/health'
},
configured_for: process.env.REDIRECT_URL
});
});
app.listen(port, () => {
console.log('='.repeat(50));
console.log('OAuth Provider for Decap CMS');
console.log('='.repeat(50));
console.log(`Server listening on port ${port}`);
console.log(`Base URL: ${process.env.BASE_URL}`);
console.log(`Redirect URL: ${process.env.REDIRECT_URL}`);
console.log('='.repeat(50));
});
Save with Ctrl+X, then Y, then Enter.
What this does:
/auth- Redirects user to GitHub for authentication/callback- Receives code from GitHub, exchanges for token/health- Health check endpoint/- Shows configuration info
Step 3.3: Create package.json
nano package.json
Paste:
{
"name": "decap-oauth-provider",
"version": "1.0.0",
"description": "OAuth provider for Decap CMS",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"simple-oauth2": "^5.0.0"
},
"engines": {
"node": ">=18.0.0"
}
}
Save with Ctrl+X, Y, Enter.
Step 3.4: Create Dockerfile
nano Dockerfile
Paste:
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package.json ./
# Install dependencies
RUN npm install --production
# Copy application
COPY server.js ./
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# Start application
CMD ["node", "server.js"]
Save with Ctrl+X, Y, Enter.
Step 3.5: Create docker-compose.yml
nano docker-compose.yml
Paste (replace the placeholder values):
version: '3.8'
services:
decap-oauth:
build: .
container_name: decap-oauth
restart: unless-stopped
networks:
- web
environment:
# Server configuration
- NODE_ENV=production
- PORT=3000
# OAuth configuration - REPLACE THESE VALUES!
- OAUTH_CLIENT_ID=YOUR_GITHUB_CLIENT_ID_HERE
- OAUTH_CLIENT_SECRET=YOUR_GITHUB_CLIENT_SECRET_HERE
- BASE_URL=https://oauth.mysite.com
- REDIRECT_URL=https://www.mysite.com
labels:
# Enable Traefik
- "traefik.enable=true"
# Router configuration
- "traefik.http.routers.decap-oauth.rule=Host(`oauth.mysite.com`)"
- "traefik.http.routers.decap-oauth.entrypoints=websecure"
- "traefik.http.routers.decap-oauth.tls.certresolver=letsencrypt"
# Service configuration
- "traefik.http.services.decap-oauth.loadbalancer.server.port=3000"
networks:
web:
external: true
IMPORTANT - Replace these values:
- OAUTH_CLIENT_ID: Your GitHub Client ID from Part 1
- OAUTH_CLIENT_SECRET: Your GitHub Client Secret from Part 1
- BASE_URL: Your OAuth provider URL (e.g.,
https://oauth.mysite.com) - REDIRECT_URL: Your website URL (e.g.,
https://www.mysite.com) - Host in labels: Change
oauth.mysite.comto your actual OAuth domain
Example with real values:
- OAUTH_CLIENT_ID=Iv1.abc123def456
- OAUTH_CLIENT_SECRET=abc123def456ghi789jkl012
- BASE_URL=https://oauth.boeygembos.be
- REDIRECT_URL=https://www.boeygembos.be
Save with Ctrl+X, Y, Enter.
Step 3.6: Build and Start Container
# Build the Docker image
docker compose build
# Start the container
docker compose up -d
# Check if it's running
docker ps | grep decap-oauth
Should show container running.
Step 3.7: Check Logs
docker compose logs
Should see something like:
==================================================
OAuth Provider for Decap CMS
==================================================
Server listening on port 3000
Base URL: https://oauth.mysite.com
Redirect URL: https://www.mysite.com
==================================================
If you see errors, check Troubleshooting section.
Step 3.8: Test OAuth Provider
# Health check
curl https://oauth.mysite.com/health
# Should return:
# {"status":"healthy","message":"OAuth provider is running","uptime":123.45}
# Check configuration
curl https://oauth.mysite.com/
# Should return info about endpoints
If you get errors or 404:
- Wait 1-2 minutes for SSL certificate generation
- Check Traefik dashboard:
https://traefik.yourvps.com - Verify DNS is resolving:
dig oauth.mysite.com +short
Part 4: Configure Decap CMS
Step 4.1: Locate Decap CMS Config
In your website repository, find the Decap CMS configuration file.
Common locations:
public/admin/config.yml
static/admin/config.yml
admin/config.yml
src/admin/config.yml
Find it:
# In your local project
find . -name "config.yml" -path "*/admin/*"
Step 4.2: Update Backend Configuration
Edit your config.yml file:
Before (local development only):
backend:
name: github
repo: username/repository
branch: main
After (works in production):
backend:
name: github
repo: username/repository
branch: main
base_url: https://oauth.mysite.com
auth_endpoint: auth
Important:
- Replace
username/repositorywith your actual GitHub repo - Replace
https://oauth.mysite.comwith your actual OAuth provider URL auth_endpoint: authpoints to the/authroute in your OAuth provider- Keep
branch:set to whatever branch you use
Full example:
backend:
name: github
repo: boeygembos/boeygembos.github.io
branch: release
base_url: https://oauth.boeygembos.be
auth_endpoint: auth
media_folder: "public/images/uploads"
public_folder: "/images/uploads"
collections:
- name: "pages"
label: "Pages"
files:
- label: "Home"
name: "home"
file: "content/home.md"
fields:
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
Step 4.3: Commit and Deploy
# In your local project
git add public/admin/config.yml # or wherever your config is
git commit -m "Add OAuth authentication for production CMS"
git push origin main # or your branch name
If you have GitHub Actions CI/CD set up, wait for deployment (~2 minutes).
If deploying manually:
# Build locally
npm run build
# Upload to VPS (adjust paths)
scp -r dist/* root@YOUR_VPS_IP:/opt/docker/websites/yoursite/html/
# Or rebuild on VPS if that's your workflow
Testing
Test 1: Access CMS Admin
- Open your browser
- Go to:
https://www.mysite.com/admin/ - You should see the Decap CMS interface
- Look for “Login with GitHub” button
If you don’t see the admin page:
- Check if
/admin/directory exists in your deployed files - Verify Decap CMS is properly configured in your project
- Check browser console (F12) for errors
Test 2: Authenticate with GitHub
- Click “Login with GitHub” button
- Should redirect to
oauth.mysite.com/auth - Then redirects to GitHub
- You’ll see GitHub authorization page:
- “Authorize [Your App Name]”
- Shows permissions requested (repo, user)
- Click “Authorize [username]”
- Should redirect back to your CMS
- You should now be logged in! ✅
What you should see:
- CMS loads your content
- You can browse collections
- You can edit entries
- “Publish” or “Save” buttons are available
Test 3: Edit and Save Content
- In the CMS, click on a collection (e.g., “Pages”)
- Click on an entry or create a new one
- Make a change (edit text, add content)
- Click “Publish” or “Save”
- Should see success message
- Check GitHub repository - should see a new commit!
Verify the commit:
- Go to your GitHub repo
- Check recent commits
- Should see a commit from your GitHub account
- Commit message will be from Decap CMS
Test 4: Verify Changes on Live Site
- Wait a few minutes (if using CI/CD)
- Visit your live website
- Changes should be visible
- Content you edited should be updated
If changes don’t appear:
- Check if CI/CD workflow ran successfully
- Verify deployment completed
- Clear browser cache and refresh
- Check if you edited the correct branch
Test 5: OAuth Provider Health
# From your computer or VPS
# Health check
curl https://oauth.mysite.com/health
# Should return:
{
"status": "healthy",
"message": "OAuth provider is running",
"uptime": 12345.67
}
# Check container logs
ssh root@YOUR_VPS_IP
cd /opt/docker/decap-oauth
docker compose logs --tail=50
Look for log entries when you authenticated:
Auth request received, redirecting to GitHub
Authentication successful, sending token to CMS
Troubleshooting
Issue 1: Can’t Access /admin/ Page
Symptoms:
https://www.mysite.com/admin/shows 404- CMS interface doesn’t load
Solutions:
- Check if admin files exist:
# On VPS
ls -la /opt/docker/websites/yoursite/html/admin/
Should see index.html and config.yml.
- Verify build includes admin files:
# Locally, after building
ls -la dist/admin/ # or build/admin/
- Check nginx configuration (if using SPA routing):
Make sure your nginx config serves static files correctly:
location /admin/ {
try_files $uri $uri/ /admin/index.html;
}
Issue 2: “Failed to Load Config” Error
Symptoms:
- Admin page loads but shows error
- “Error loading config.yml”
- Blank page with errors in console
Solutions:
- Check config.yml syntax:
# Validate YAML
python3 -c "import yaml; yaml.safe_load(open('public/admin/config.yml'))"
- Verify config.yml is deployed:
# On VPS
cat /opt/docker/websites/yoursite/html/admin/config.yml
- Check browser console (F12 → Console tab):
- Look for specific error messages
- YAML parsing errors
- 404 errors loading config
- Common config errors:
- Incorrect indentation (YAML is indent-sensitive)
- Missing required fields
- Incorrect backend configuration
Issue 3: OAuth Provider Returns 404
Symptoms:
- Clicking “Login with GitHub” redirects to OAuth provider
- Shows “404 Not Found” or “Cannot GET /auth”
Solutions:
- Check DNS resolves:
dig oauth.mysite.com +short
# Should return your VPS IP
- Check container is running:
ssh root@YOUR_VPS_IP
docker ps | grep decap-oauth
- Check container logs:
cd /opt/docker/decap-oauth
docker compose logs
Look for errors starting up.
- Test directly:
curl https://oauth.mysite.com/health
If this works, OAuth provider is running. Issue is with routing.
- Check Traefik configuration:
- Visit:
https://traefik.yourvps.com - Look for
decap-oauthin HTTP Routers - Verify route is configured correctly
- Verify SSL certificate:
curl -I https://oauth.mysite.com/health
Should return 200 OK with valid SSL.
Issue 4: GitHub Authorization Fails
Symptoms:
- Redirects to GitHub successfully
- After authorizing, get error
- “Bad credentials” or “Invalid client”
Solutions:
- Verify Client ID and Secret:
Check docker-compose.yml has correct values:
cd /opt/docker/decap-oauth
cat docker-compose.yml | grep OAUTH_CLIENT
- Regenerate Client Secret:
If you think secret is wrong:
- Go to: https://github.com/settings/developers
- Find your OAuth App
- Click “Generate a new client secret”
- Update docker-compose.yml with new secret
- Restart container:
docker compose restart
- Check callback URL matches:
In GitHub OAuth App settings:
Authorization callback URL: https://oauth.mysite.com/callback
Must EXACTLY match what’s in your config.
- Check container logs:
docker compose logs
Look for specific error messages from GitHub.
Issue 5: CORS Errors
Symptoms:
- Browser console shows CORS error
- “Access-Control-Allow-Origin” error
- Authentication fails silently
Solutions:
- Verify REDIRECT_URL matches website:
In docker-compose.yml:
- REDIRECT_URL=https://www.mysite.com
Must match your actual site URL EXACTLY:
- Same protocol (https)
- Same subdomain (www or not)
- No trailing slash
- Check CORS configuration in server.js:
app.use(cors({
origin: true, // Allows all origins
credentials: true
}));
This should already be correct in the code provided.
- Browser console details:
Open browser console (F12) when error occurs:
- Read the full error message
- Note which origin is being blocked
- Verify it matches your site
Issue 6: Organization Access Restrictions
Symptoms:
- Authentication succeeds
- Can’t save changes
- Error: “organization has enabled OAuth App access restrictions”
Solution:
Your repository is in a GitHub organization with OAuth restrictions:
- Go to: https://github.com/organizations/YOUR_ORG/settings/oauth_application_policy
- Or: https://github.com/settings/connections/applications
- Find your OAuth App
- Grant organization access
- If you’re org owner, access grants immediately
- If not, request access (admin must approve)
See detailed instructions in the multi-site guide.
Issue 7: Authentication Works But Can’t Save
Symptoms:
- Successfully logs in
- Can view content
- “Publish” or “Save” fails
- Error: “Failed to persist entry”
Solutions:
- Check GitHub permissions:
User must have write access to the repository:
- Go to:
https://github.com/username/repository/settings/access - Verify your account has “Write” or “Admin” access
- If not, add yourself as a collaborator
- Check branch name:
In config.yml:
backend:
branch: main # Must match actual branch name
If your default branch is master, change it to master.
- Check repository exists:
Verify the repo in config.yml is correct:
backend:
repo: username/repository # Check spelling!
- Check OAuth app scope:
OAuth app must have repo scope. Verify in server.js:
scope: 'repo,user', // 'repo' is required
Issue 8: Container Won’t Start
Symptoms:
docker compose up -dfails- Container keeps restarting
docker psshows container exiting
Solutions:
- Check logs:
docker compose logs
Look for specific error messages.
- Common startup errors:
Syntax error in server.js:
SyntaxError: Unexpected token
→ Check server.js for typos, missing commas, etc.
Missing dependencies:
Error: Cannot find module 'express'
→ Rebuild container: docker compose build --no-cache
Port already in use:
Error: listen EADDRINUSE: address already in use :::3000
→ Another container using port 3000 → Check: docker ps | grep 3000
- Test server.js syntax:
node -c server.js
Should return nothing if syntax is valid.
- Rebuild from scratch:
docker compose down
docker compose build --no-cache
docker compose up -d
docker compose logs -f
Security Considerations
Protecting Credentials
Client Secret is sensitive!
✅ DO:
- Store in
docker-compose.ymlonly - Set file permissions:
chmod 600 docker-compose.yml - Never commit to git with real secrets
- Keep backups encrypted
- Rotate periodically (every 6-12 months)
❌ DON’T:
- Hardcode in server.js
- Commit to public repositories
- Share via email/chat
- Log to console
- Store in plain text files
File Permissions
cd /opt/docker/decap-oauth
# Restrict access to docker-compose.yml
chmod 600 docker-compose.yml
# Verify
ls -l docker-compose.yml
# Should show: -rw------- (read/write for owner only)
Repository Access Control
Who can edit content:
- Anyone with write access to the GitHub repository
- Control via:
https://github.com/username/repo/settings/access
Best practices:
- Only add necessary collaborators
- Use teams for organization repos
- Remove access for former team members
- Review access regularly
OAuth App Security
Monitor your OAuth app:
- Go to: https://github.com/settings/developers
- Click your OAuth App
- Check Recent Delivery activity
- Look for unusual patterns
If compromised:
- Click “Revoke all user tokens”
- Generate new Client Secret
- Update docker-compose.yml
- Restart container
- All users need to re-authenticate
Network Security
Already secured by your setup:
- ✅ OAuth provider only accessible via HTTPS (Traefik)
- ✅ No direct port exposure (internal port 3000)
- ✅ SSL certificates automatic (Let’s Encrypt)
- ✅ Container isolated on Docker network
Additional measures:
- Firewall only allows ports 80, 443 (already done)
- SSH key-only authentication (already done)
- fail2ban monitoring (already done)
Maintenance
Regular Tasks
Check OAuth Provider Health
Weekly:
# From anywhere
curl https://oauth.mysite.com/health
# Should return healthy status
Monthly:
# SSH to VPS
cd /opt/docker/decap-oauth
# Check container status
docker compose ps
# Check logs for errors
docker compose logs --tail=100 | grep -i error
# Check disk space
df -h
Monitor Usage
View authentication activity:
cd /opt/docker/decap-oauth
docker compose logs | grep "Auth request"
docker compose logs | grep "Authentication successful"
Count authentications:
docker compose logs | grep "Authentication successful" | wc -l
Update Dependencies
Every 3-6 months:
- Update package.json:
cd /opt/docker/decap-oauth
nano package.json
Update versions to latest:
"dependencies": {
"express": "^4.19.2", // Check latest
"cors": "^2.8.5",
"simple-oauth2": "^5.1.0" // Check latest
}
- Rebuild container:
docker compose down
docker compose build --no-cache
docker compose up -d
- Test authentication:
- Go to your CMS
- Log in
- Verify it works
Check for updates:
- express: https://www.npmjs.com/package/express
- cors: https://www.npmjs.com/package/cors
- simple-oauth2: https://www.npmjs.com/package/simple-oauth2
Backup Configuration
Create backup:
cd /opt/docker/decap-oauth
# Create backup directory
mkdir -p ~/backups/decap-oauth
# Backup files (excluding secrets for security)
cp server.js ~/backups/decap-oauth/server.js.backup
cp package.json ~/backups/decap-oauth/package.json.backup
cp Dockerfile ~/backups/decap-oauth/Dockerfile.backup
# Note: Don't backup docker-compose.yml with secrets!
# Instead, store secrets separately and securely
Restore from backup:
cd /opt/docker/decap-oauth
docker compose down
# Restore files
cp ~/backups/decap-oauth/*.backup .
# Manually recreate docker-compose.yml with secrets
nano docker-compose.yml
docker compose up -d
Rotating Secrets
When to rotate:
- Every 6-12 months (scheduled)
- If secret may have been compromised
- When team members leave
- Before security audits
How to rotate:
- Generate new secret on GitHub:
- https://github.com/settings/developers
- Find your OAuth App
- “Generate a new client secret”
- Copy new secret
- Update docker-compose.yml:
cd /opt/docker/decap-oauth
nano docker-compose.yml
# Update OAUTH_CLIENT_SECRET
- Restart container:
docker compose restart
- Test:
- Users may need to re-authorize
- Test authentication flow works
Container Logs
View recent logs:
docker compose logs --tail=50
Follow logs live:
docker compose logs -f
Filter logs:
# Only errors
docker compose logs | grep -i error
# Only auth events
docker compose logs | grep -i auth
# Last hour
docker compose logs --since=1h
Log rotation:
- Docker automatically rotates logs
- Default: 10MB max per file, keeps 3 files
- No manual management needed