Outdated dependencies are a compounding problem. Each week you ignore them, the upgrade diff grows and the risk of breaking changes increases. For remote teams, there’s no “Friday afternoon let’s update dependencies” session — you need a system that proposes updates automatically and makes merging them low-friction.
Renovate (Most Powerful, Self-Hostable)
Renovate is the most capable automated dependency updater. It groups updates, understands monorepos, respects your merge schedule, and auto-merges low-risk updates.
Option A: GitHub App (easiest)
- Install the Renovate GitHub App
- Add
renovate.jsonto your repo root:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"timezone": "America/New_York",
"schedule": ["after 9am and before 5pm every weekday"],
"prHourlyLimit": 2,
"prConcurrentLimit": 10,
"labels": ["dependencies"],
"assignees": ["alice", "bob"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"matchPackageNames": ["react", "react-dom"],
"groupName": "React packages",
"schedule": ["on the first day of the month"]
},
{
"matchPackagePatterns": ["^@types/"],
"groupName": "TypeScript type definitions",
"automerge": true
},
{
"matchManagers": ["terraform"],
"groupName": "Terraform providers",
"schedule": ["on the first day of the month"]
}
],
"vulnerabilityAlerts": {
"labels": ["security"],
"assignees": ["security-team"],
"schedule": ["at any time"]
}
}
Option B: Self-hosted with Docker
# docker-compose.yml (for your Renovate runner server)
version: "3.8"
services:
renovate:
image: renovate/renovate:latest
volumes:
- /tmp/renovate:/tmp/renovate
environment:
- RENOVATE_TOKEN=${GITHUB_TOKEN}
- RENOVATE_PLATFORM=github
- LOG_LEVEL=info
command: >
--autodiscover=true
--autodiscover-filter=your-org/*
Run on a schedule:
# crontab
0 8 * * 1-5 docker run --rm \
-e RENOVATE_TOKEN=$GITHUB_TOKEN \
renovate/renovate:latest \
--autodiscover=true \
--autodiscover-filter=your-org/*
Renovate for Monorepos
When your team uses a monorepo, Renovate handles multiple package managers in a single run. Set enabledManagers to keep things explicit:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"enabledManagers": ["npm", "docker", "github-actions", "terraform"],
"ignorePaths": ["**/node_modules/**", "**/fixtures/**"],
"packageRules": [
{
"matchPaths": ["services/api/**"],
"groupName": "API service dependencies",
"assignees": ["backend-team"]
},
{
"matchPaths": ["services/frontend/**"],
"groupName": "Frontend dependencies",
"assignees": ["frontend-team"]
}
]
}
This ensures PRs land in front of the right team without everyone getting notified for changes outside their domain.
Dependabot (GitHub Native)
Dependabot is built into GitHub, requires no infrastructure, and is sufficient for most teams. It’s less flexible than Renovate but has zero setup friction.
# .github/dependabot.yml
version: 2
updates:
# npm dependencies
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "America/New_York"
labels:
- "dependencies"
- "javascript"
reviewers:
- "frontend-team"
open-pull-requests-limit: 10
groups:
react:
patterns:
- "react"
- "react-dom"
- "@types/react*"
testing:
patterns:
- "jest*"
- "@testing-library/*"
- "vitest*"
# Go modules
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
day: "tuesday"
labels:
- "dependencies"
- "go"
reviewers:
- "backend-team"
# Docker base images
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "monthly"
labels:
- "dependencies"
- "docker"
# Terraform providers
- package-ecosystem: "terraform"
directory: "/infra/terraform"
schedule:
interval: "monthly"
labels:
- "dependencies"
- "infrastructure"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "ci"
Auto-merge Dependabot patch updates via GitHub Actions:
# .github/workflows/dependabot-automerge.yml
name: Dependabot Auto-merge
on: pull_request
permissions:
contents: write
pull-requests: write
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Fetch Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2
- name: Auto-merge patch and minor updates
if: |
steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
steps.metadata.outputs.update-type == 'version-update:semver-minor'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWASP Dependency-Check (Vulnerability Scanning)
Renovate and Dependabot handle version updates. OWASP Dependency-Check scans for known vulnerabilities in your current dependencies, even if you’re up-to-date:
# .github/workflows/dependency-check.yml
name: Dependency Vulnerability Scan
on:
push:
branches: [main]
schedule:
- cron: '0 8 * * 1' # Weekly
jobs:
dependency-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'your-project'
path: '.'
format: 'HTML'
args: >
--enableRetired
--failOnCVSS 7
--suppression suppression.xml
- name: Upload results
uses: actions/upload-artifact@v4
if: always()
with:
name: dependency-check-report
path: ${{ github.workspace }}/reports
Create suppression.xml to suppress false positives:
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress>
<notes>False positive - this CVE affects a different component</notes>
<cve>CVE-2023-12345</cve>
</suppress>
</suppressions>
Running OWASP Dependency-Check Locally
For local scans before pushing, run the CLI directly:
# Download and run the CLI scanner
VERSION="9.0.9"
curl -sL "https://github.com/jeremylong/DependencyCheck/releases/download/v${VERSION}/dependency-check-${VERSION}-release.zip" -o dc.zip
unzip dc.zip -d /opt/dependency-check
# Scan a Node project
/opt/dependency-check/bin/dependency-check.sh \
--project "myapp" \
--scan ./node_modules \
--format HTML \
--out ./reports \
--failOnCVSS 7
# Update the NVD database cache (do this weekly)
/opt/dependency-check/bin/dependency-check.sh --updateonly
The NVD database download takes several minutes the first time. After that, incremental updates are fast. Cache it on your CI runner to avoid re-downloading on every job.
Snyk (Security-First)
Snyk combines vulnerability detection with automated fix PRs and license compliance checking:
# Install CLI
npm install -g snyk
# Authenticate
snyk auth
# Scan project
snyk test --severity-threshold=high
# Watch project for new vulnerabilities (CI)
snyk monitor
# Fix vulnerabilities automatically
snyk fix
Add to CI:
# .github/workflows/snyk.yml
name: Snyk Security Scan
on: [push, pull_request]
jobs:
snyk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=upgradable
Snyk for Container Images
Snyk also scans Docker images for OS-level CVEs, which OWASP Dependency-Check misses:
# Scan a container image
snyk container test your-org/your-app:latest \
--file=Dockerfile \
--severity-threshold=high
# Monitor an image in the Snyk dashboard
snyk container monitor your-org/your-app:latest \
--project-name="production-api"
Add container scanning to your CI pipeline after the image build step:
- name: Scan container image
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: your-org/your-app:${{ github.sha }}
args: --severity-threshold=high
License Compliance
Track open source license compliance before it becomes a legal issue:
# Node.js
npm install -g license-checker
license-checker --summary --excludePrivatePackages
# Output licenses in CSV for legal review
license-checker --csv --out licenses.csv
# Fail on GPL licenses in commercial projects
license-checker \
--failOn "GPL-2.0;GPL-3.0;AGPL-3.0" \
--excludePrivatePackages
# Python
pip install pip-licenses
pip-licenses --format=markdown --with-urls
# Go
go install github.com/google/go-licenses@latest
go-licenses check ./... --disallowed_types=restricted,forbidden
Automating License Reports in CI
Generate a license report on every PR so legal review can happen before merge rather than after:
# .github/workflows/license-check.yml
name: License Compliance
on: [pull_request]
jobs:
licenses:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Generate license report
run: |
npm install -g license-checker
license-checker \
--failOn "GPL-2.0;GPL-3.0;AGPL-3.0;LGPL-2.0;LGPL-3.0" \
--excludePrivatePackages \
--csv \
--out licenses.csv
- name: Upload license report
uses: actions/upload-artifact@v4
with:
name: license-report
path: licenses.csv
Keep a license-allowlist.txt in your repo with approved licenses. Run license-checker against the allowlist on every merge to main:
# Strict allowlist approach
license-checker \
--onlyAllow "MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0;0BSD;CC0-1.0" \
--excludePrivatePackages
Tool Comparison
| Tool | Type | Self-Hosted | Best For |
|---|---|---|---|
| Renovate | Update automation | Yes | Fine-grained control, monorepos |
| Dependabot | Update automation | No | GitHub-native, quick setup |
| OWASP DC | Vulnerability scan | Yes | CVE scanning, offline |
| Snyk | Security + fixes | No | Security-first teams |
Use Renovate or Dependabot for updates, OWASP or Snyk for vulnerability scanning — they complement each other.
Dependency Update Workflow for Async Remote Teams
The biggest failure mode for remote teams isn’t tooling — it’s process. PRs opened by Renovate or Dependabot sit unreviewed for days because no one owns them. Fix this with explicit ownership rules.
Set a standing agenda item in your weekly async update (Loom, Notion, or Slack thread) for dependency PR status. The assigned reviewer for the week checks pending dependency PRs each Monday and either merges, comments with a block reason, or escalates to the full team. This simple rotation prevents dependency debt from silently accumulating.
Use branch protection rules to enforce that dependency PRs pass CI before merge:
# .github/branch-protection.json (configure via gh CLI or Terraform)
{
"required_status_checks": {
"strict": true,
"contexts": [
"test",
"lint",
"security-scan"
]
},
"required_pull_request_reviews": {
"dismiss_stale_reviews": true,
"required_approving_review_count": 1
}
}
For dependency PRs specifically, one approval is usually sufficient — the CI pipeline is the real gatekeeper. Reserve two-approval requirements for application code changes.
Tracking Dependency Health Over Time
Point-in-time scans don’t tell you whether your dependency posture is improving or degrading. Add a weekly dependency health report to your team dashboard:
#!/bin/bash
# dependency-health.sh — outputs a summary suitable for Slack or Notion
echo "=== Dependency Health Report $(date +%Y-%m-%d) ==="
# Count open Dependabot/Renovate PRs
echo "Open dependency PRs:"
gh pr list \
--label dependencies \
--state open \
--json number,title,createdAt \
--jq '.[] | "\(.number): \(.title) (opened \(.createdAt[:10]))"'
echo ""
echo "PRs older than 14 days:"
gh pr list \
--label dependencies \
--state open \
--json number,title,createdAt \
--jq '.[] | select((.createdAt | fromdateiso8601) < (now - 1209600)) | "\(.number): \(.title)"'
Post this report to a #dependency-updates Slack channel every Monday. The act of making the backlog visible reduces it — teams that track open dependency PRs consistently close them faster than teams that rely on email notifications alone.
Choosing the Right Stack for Your Team Size
For a team of 1–5 engineers, Dependabot plus Snyk free tier covers the essentials with zero infrastructure overhead. Add license-checker to CI and you have full coverage.
For teams of 6–20 engineers working across multiple repos or a monorepo, Renovate’s grouping and scheduling control saves significant review time. Self-host it on a small VM or run it via GitHub Actions on a cron schedule.
For teams of 20+ or those in regulated industries (fintech, healthcare), add OWASP Dependency-Check for offline CVE scanning and generate formal license reports quarterly using go-licenses or pip-licenses depending on your stack. Snyk’s paid tier adds policy enforcement, so security requirements can be defined once and applied across all repos automatically.
Related Reading
- Remote Team Git Hooks Standardization Guide
- Best Tools for Remote Team Error Tracking
- How to Automate Changelog Generation
- Best Bug Tracking Setup for a 7-Person Remote QA Team
Related Articles
- How to Set Up Renovate for Dependency Updates
- How to Track Project Dependencies Remote Team
- Remote DevOps Team Dependency Update Workflow for
- Best Remote Collaboration Tool for Technical Architects
- Productivity Tracking Tools for Remote Teams 2026
Built by theluckystrike — More at zovo.one