Linkwarden: Self-Hosted Bookmark Manager with Archives
Save, organize, and preserve your bookmarks. Automatic archiving and collaboration features.
Introduction
Linkwarden is a powerful, self-hosted bookmark manager that goes beyond simple link storage. Built with modern technologies (Next.js, Prisma, PostgreSQL), Linkwarden automatically archives web pages, captures screenshots, generates PDFs, and makes everything searchable. With over 5,000 GitHub stars and active development, it has become the premier solution for individuals and teams who need reliable, long-term bookmark preservation.
Why Choose Linkwarden?
Permanent Link Preservation
The web is ephemeral - pages disappear, content changes, and links break. Linkwarden creates permanent archives of every bookmarked page with screenshots, PDFs, and full HTML snapshots. Even if the original site goes offline, your archive remains accessible.
Complete Privacy and Ownership
Unlike cloud bookmark services like Raindrop, Pocket, or Instapaper that monetize your browsing data, Linkwarden keeps everything on your infrastructure. Your reading habits, research topics, and saved content remain completely private.
Powerful Search and Organization
Full-text search across all archived content means you can find that article you read months ago, even if you don’t remember the title. Rich tagging, collections, and filtering make organization effortless.
Zero Cost, Premium Features
Commercial bookmark services charge $5-10/month for features like full-text search, PDF archiving, and unlimited bookmarks. A typical homelab setup costs $300-500 one-time, breaking even in under a year while offering unlimited storage and features.
Production-Grade Features
- Automatic webpage archiving with screenshots and PDFs
- Browser extensions for Chrome, Firefox, Safari, Edge
- Full-text search across archived content
- Rich tagging and collections
- Collaborative sharing with teams
- Mobile-responsive web interface
- Dark mode support
- API for automation
- Duplicate detection
- Import from existing bookmark services
- RSS feed monitoring
- Public sharing links
Understanding the Architecture
Linkwarden uses a modern architecture optimized for archiving:
Internet/Local Network
│
▼
┌───────────────┐
│ Web Browser │ (Bookmark management, search)
│ + Extension │ (Quick save from any page)
└───────┬───────┘
│
▼
┌───────────────────┐
│ Linkwarden App │ (Next.js application)
│ Port 3000 │
└───────┬───────────┘
│
┌───┴────┬──────────┬───────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌────────┐ ┌───────┐ ┌──────┐ ┌────────┐
│Postgres│ │ Minio │ │Chrome│ │ Search │
│Database│ │ S3 │ │ PDF │ │ Index │
└────────┘ └───────┘ └──────┘ └────────┘
│ │ │ │
└──────────┴─────────┴──────────┘
│
▼
┌───────────────┐
│ Storage │
│ - Screenshots│
│ - PDFs │
│ - HTML │
└───────────────┘ Components:
- Frontend: Next.js React application
- Database: PostgreSQL for metadata and search
- Object Storage: MinIO (S3-compatible) for archives
- Chromium: Headless browser for screenshots and PDFs
- Search Index: Full-text search across content
Prerequisites and System Requirements
Hardware Specifications
Minimum Configuration
- RAM: 4GB (archiving requires significant memory)
- CPU: 2 cores
- Storage: 50GB+ (archives grow quickly)
- Network: Good internet for archiving external content
Recommended Configuration
- RAM: 8GB
- CPU: 4+ cores (faster archiving)
- Storage: 500GB+ SSD (for large archives)
- Network: Gigabit ethernet
For teams (10+ users):
- RAM: 16GB
- CPU: 8+ cores
- Storage: 1TB+ with backup
- Dedicated database and storage servers
Software Requirements
- Docker Engine v24+ with docker-compose
- Linux (Ubuntu 22.04/24.04 recommended)
- 20GB+ free space for Docker images
- PostgreSQL 14+
- MinIO or S3-compatible storage
Pre-Deployment Checklist
# Verify Docker
docker --version
docker compose version
# Check resources
free -h
df -h /var/lib/docker
# Test connectivity
ping -c 3 google.com Installation and Deployment
Step 1: Environment Preparation
# Create directory
mkdir -p ~/linkwarden
cd ~/linkwarden
# Generate secure secrets
NEXTAUTH_SECRET=$(openssl rand -base64 32)
POSTGRES_PASSWORD=$(openssl rand -base64 24)
MINIO_ROOT_PASSWORD=$(openssl rand -base64 24)
# Create environment file
cat > .env <<EOF
# Application
NEXTAUTH_URL=http://YOUR-SERVER-IP:3000
NEXTAUTH_SECRET=$NEXTAUTH_SECRET
NODE_ENV=production
# Database
DATABASE_URL=postgresql://linkwarden:${POSTGRES_PASSWORD}@linkwarden-db:5432/linkwarden
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
# Storage (MinIO S3)
NEXT_PUBLIC_STORAGE_PROVIDER=s3
MINIO_ENDPOINT=linkwarden-minio:9000
MINIO_ROOT_USER=linkwarden
MINIO_ROOT_PASSWORD=$MINIO_ROOT_PASSWORD
MINIO_ACCESS_KEY=linkwarden
MINIO_SECRET_KEY=$MINIO_ROOT_PASSWORD
SPACES_BUCKET_NAME=linkwarden
SPACES_REGION=us-east-1
SPACES_ENDPOINT=http://linkwarden-minio:9000
# Archiving
NEXT_PUBLIC_AUTOSCROLL_TIMEOUT=30
NEXT_PUBLIC_ARCHIVE_LIMIT=5000
NEXT_PUBLIC_MAX_FILE_SIZE=30000000
# Browser for archiving
CHROMIUM_PATH=/usr/bin/chromium
BROWSER_TIMEOUT=60000
# Registration
NEXT_PUBLIC_DISABLE_REGISTRATION=false
# Features
NEXT_PUBLIC_ENABLE_PUBLIC_LINKS=true
EOF
chmod 600 .env Important: Replace YOUR-SERVER-IP with your actual server IP.
Step 2: Create Docker Compose Configuration
Create docker-compose.yml:
version: "3.8"
services:
# PostgreSQL Database
linkwarden-db:
image: postgres:16-alpine
container_name: linkwarden-db
restart: unless-stopped
environment:
- POSTGRES_DB=linkwarden
- POSTGRES_USER=linkwarden
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- linkwarden
healthcheck:
test: ["CMD-SHELL", "pg_isready -U linkwarden -d linkwarden"]
interval: 10s
timeout: 5s
retries: 5
# MinIO S3 Storage
linkwarden-minio:
image: minio/minio:latest
container_name: linkwarden-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
volumes:
- minio_data:/data
networks:
- linkwarden
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
# Linkwarden Application
linkwarden:
image: ghcr.io/linkwarden/linkwarden:latest
container_name: linkwarden
restart: unless-stopped
depends_on:
linkwarden-db:
condition: service_healthy
linkwarden-minio:
condition: service_healthy
environment:
- DATABASE_URL=${DATABASE_URL}
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- NEXTAUTH_URL=${NEXTAUTH_URL}
- NEXT_PUBLIC_STORAGE_PROVIDER=${NEXT_PUBLIC_STORAGE_PROVIDER}
- SPACES_ENDPOINT=${SPACES_ENDPOINT}
- SPACES_REGION=${SPACES_REGION}
- SPACES_BUCKET_NAME=${SPACES_BUCKET_NAME}
- SPACES_KEY=${MINIO_ACCESS_KEY}
- SPACES_SECRET=${MINIO_SECRET_KEY}
- NEXT_PUBLIC_DISABLE_REGISTRATION=${NEXT_PUBLIC_DISABLE_REGISTRATION}
- NEXT_PUBLIC_ENABLE_PUBLIC_LINKS=${NEXT_PUBLIC_ENABLE_PUBLIC_LINKS}
ports:
- "3000:3000"
volumes:
- linkwarden_data:/data/data
networks:
- linkwarden
volumes:
postgres_data:
minio_data:
linkwarden_data:
networks:
linkwarden:
driver: bridge Step 3: Deploy Linkwarden
# Pull all images
docker compose pull
# Start services
docker compose up -d
# Monitor startup (wait 2-3 minutes)
docker compose logs -f
# Press Ctrl+C to exit
# Verify all containers running
docker compose ps All three containers should show “Up (healthy)” status.
Step 4: Initial Setup
Navigate to http://YOUR-SERVER-IP:3000
First-time setup:
1. Click "Create an Account"
2. Enter details:
- Name: Your Name
- Email: [email protected]
- Password: Strong password (12+ characters)
3. Click "Sign Up"
4. You're logged in automatically Important first steps:
1. Settings → General:
- Archive by default: Yes
- Screenshot by default: Yes
- PDF by default: Yes
- Auto-tag: Yes
2. Settings → Archive:
- Archive Timeout: 30 seconds
- Max Archive Size: 30 MB
- Screenshot Quality: High
3. Settings → Privacy:
- Disable registration: Yes (after creating accounts)
- Public links: Enable if needed Step 5: Create Your First Collection
Collections organize bookmarks by topic:
1. Click "Collections" → "Create Collection"
2. Name: "Tech Articles"
3. Description: "Interesting technical articles and tutorials"
4. Color: Choose a color
5. Visibility: Private (or Shared for teams)
6. Save Step 6: Save Your First Bookmark
Via web interface:
1. Click "+" button → "Add Link"
2. Paste URL: https://example.com/article
3. Options:
- Collection: Tech Articles
- Tags: programming, tutorial
- Take Screenshot: Yes
- Create PDF: Yes
- Create Readable: Yes (removes ads, formats for reading)
4. Click "Add"
5. Linkwarden archives the page (30-60 seconds)
6. Bookmark appears with screenshot and PDF Step 7: Install Browser Extension
For Chrome/Edge/Brave:
1. Visit Chrome Web Store
2. Search "Linkwarden"
3. Click "Add to Chrome"
4. Click extension icon
5. Settings → Server URL: http://YOUR-SERVER-IP:3000
6. Login with credentials For Firefox:
1. Visit Firefox Add-ons
2. Search "Linkwarden"
3. Click "Add to Firefox"
4. Configure server URL
5. Login Using the extension:
While browsing:
1. Click Linkwarden extension icon
2. Review page details (auto-populated)
3. Select collection
4. Add tags
5. Enable archiving options
6. Click "Save"
7. Bookmark archived in background Step 8: Configure HTTPS (Production)
Using Caddy:
linkwarden.yourdomain.com {
reverse_proxy linkwarden:3000
encode gzip
} Using Nginx:
server {
listen 443 ssl http2;
server_name linkwarden.yourdomain.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
} Update .env:
NEXTAUTH_URL=https://linkwarden.yourdomain.com Restart:
docker compose restart linkwarden Browser Extensions and Quick Save
Extension Features
One-click saving:
- Current page URL auto-filled
- Title and description extracted
- Suggested tags based on content
- Collection quick-select
- Archiving options toggle
Context menu integration:
Right-click on any link:
→ Save to Linkwarden
→ Quick save (default collection)
→ Save with options Keyboard shortcuts:
Ctrl+Shift+L (Cmd+Shift+L on Mac)
→ Quick save current page
Ctrl+Shift+K
→ Save with options dialog Mobile Bookmarklet
For mobile browsers without extensions:
1. Settings → Bookmarklet
2. Drag bookmarklet to bookmarks bar
3. On mobile: Create bookmark with provided JavaScript code
4. When browsing, tap bookmarklet to save page Link Preservation and Archiving
Archive Types
Linkwarden creates three types of archives:
1. Screenshot Archive
- Full-page screenshot
- Captures visual appearance
- Useful for design references
- Shows page as it appeared
2. PDF Archive
- Printable PDF format
- Preserves layout and formatting
- Searchable text
- Easy sharing
3. Readable View
- Cleaned HTML content
- Removes ads and clutter
- Focuses on main content
- Better for reading
Archive Quality Settings
Configure archive quality:
Settings → Archive:
Screenshot:
├─ Quality: High (1920x1080) or Medium (1280x720)
├─ Full Page: Yes (scroll and capture entire page)
└─ Format: PNG or WebP
PDF:
├─ Paper Size: A4 or Letter
├─ Include Images: Yes
├─ Include Background Graphics: Yes
└─ Margins: Normal
Readable:
├─ Remove Ads: Yes
├─ Extract Main Content: Yes
├─ Preserve Images: Yes
└─ Format for Reading: Yes Automatic Archiving Workflow
When bookmark is saved:
1. Page URL validated
2. Metadata extracted (title, description, favicon)
3. Screenshot captured (full page scroll)
4. PDF generated
5. Readable view created
6. Full-text content indexed
7. Tags auto-suggested
8. Archive stored in S3
9. Thumbnail generated
10. Search index updated
Total time: 30-90 seconds per bookmark Manual Re-archiving
Update archives for changed content:
1. Open bookmark
2. Click "⋮" menu → "Re-archive"
3. Options:
- Update Screenshot: Yes
- Update PDF: Yes
- Update Readable: Yes
4. Click "Re-archive"
5. New archives replace old ones Collections and Collaboration
Collection Types
Private Collections:
- Visible only to you
- Perfect for personal research
- Private reading lists
- Sensitive bookmarks Shared Collections:
- Invite specific users
- Collaborative research
- Team resource lists
- Project documentation Public Collections:
- Anyone with link can view
- Public resource lists
- Community bookmarks
- Portfolio/showcase Creating Shared Collections
1. Collections → Create
2. Name: "Team Research - AI/ML"
3. Visibility: Shared
4. Invite members:
- Enter email addresses
- Set permissions:
• Can view
• Can contribute (add/edit)
• Can manage (full control)
5. Save
6. Members receive email invitation Permission Levels
| Permission | View | Add | Edit | Delete | Manage |
|---|---|---|---|---|---|
| Viewer | ✓ | ✗ | ✗ | ✗ | ✗ |
| Contributor | ✓ | ✓ | ✓ | Own only | ✗ |
| Manager | ✓ | ✓ | ✓ | ✓ | ✓ |
Use Cases for Collections
Personal:
- Reading List
- Recipe Bookmarks
- Travel Planning
- Shopping Research
- Home Improvement Ideas Professional:
- Industry News
- Competitor Analysis
- Client Resources
- Project References
- Technical Documentation Team/Family:
- Shared Family Recipes
- Vacation Planning
- Gift Ideas
- Home Projects
- Educational Resources Tagging and Organization
Tag Strategy
Hierarchical tagging:
Technology/
├─ Programming/
│ ├─ Python
│ ├─ JavaScript
│ └─ Rust
├─ DevOps/
│ ├─ Docker
│ ├─ Kubernetes
│ └─ CI/CD
└─ AI/
├─ Machine Learning
├─ LLMs
└─ Computer Vision
Productivity/
├─ Time Management
├─ Note Taking
└─ Automation Tag best practices:
- Use lowercase for consistency
- Hyphenate multi-word tags: machine-learning
- Start specific, can generalize later
- Limit to 5-10 tags per bookmark
- Create tag hierarchy for large collections
- Use autocomplete for existing tags Bulk Tagging
Apply tags to multiple bookmarks:
1. Select bookmarks (checkbox)
2. Click "Bulk Actions"
3. Add tags: tutorial, python, advanced
4. Apply
5. All selected bookmarks tagged Smart Collections (Saved Searches)
Create dynamic collections based on tags:
1. Search: tag:python AND tag:tutorial
2. Save search as "Python Tutorials"
3. Collection auto-updates as new bookmarks match
4. Always current, no manual curation needed Search and Discovery
Full-Text Search
Search across all archived content:
Search capabilities:
- Page titles
- Descriptions
- Tags
- URLs
- Full archived content (text in PDFs and screenshots via OCR)
- Comments Search operators:
Basic:
python tutorial → Find bookmarks with both words
Quoted:
"machine learning" → Exact phrase
Tag search:
tag:python → Bookmarks tagged "python"
Collection search:
collection:tech-articles → Within specific collection
URL search:
url:github.com → Bookmarks from GitHub
Date search:
date:2025-01 → Bookmarks from January 2025
Combined:
tag:python AND url:github.com → Python repos from GitHub Advanced Filters
Filter sidebar:
Filters:
├─ Collections
│ └─ Select one or more
├─ Tags
│ └─ Multi-select tags
├─ Date Range
│ ├─ Created
│ └─ Modified
├─ Archive Status
│ ├─ Archived
│ ├─ Not Archived
│ └─ Failed
└─ Type
├─ Link
├─ PDF
└─ Note Sorting Options
Sort by:
- Date Added (newest/oldest)
- Title (A-Z/Z-A)
- Last Modified
- Most Visited
- Random (discover old bookmarks) Backup and Export
What to Backup
| Component | Location | Priority | Contains |
|---|---|---|---|
| Database | PostgreSQL | Critical | All bookmarks, tags, users |
| Archives | MinIO S3 | Critical | Screenshots, PDFs, readable views |
| Configuration | .env | Important | Settings |
Built-in Export
Export all bookmarks:
1. Settings → Data
2. Click "Export"
3. Format options:
- JSON (complete data)
- HTML (browser import)
- CSV (spreadsheet)
4. Include:
- Metadata
- Tags
- Collections
- Archive links
5. Download export file Automated Backup Script
Create ~/linkwarden/backup.sh:
#!/bin/bash
BACKUP_DIR="/mnt/backups/linkwarden"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
mkdir -p "$BACKUP_DIR"
echo "Starting Linkwarden backup at $(date)"
# Backup PostgreSQL
docker exec linkwarden-db pg_dump -U linkwarden linkwarden | gzip > "$BACKUP_DIR/db-${DATE}.sql.gz"
echo "✓ Database backed up"
# Backup MinIO data
docker run --rm -v linkwarden_minio_data:/source:ro -v "$BACKUP_DIR":/backup alpine tar czf /backup/minio-${DATE}.tar.gz -C /source .
echo "✓ Archive storage backed up"
# Backup application data
docker run --rm -v linkwarden_linkwarden_data:/source:ro -v "$BACKUP_DIR":/backup alpine tar czf /backup/data-${DATE}.tar.gz -C /source .
echo "✓ Application data backed up"
# Backup configuration
cp .env "$BACKUP_DIR/env-${DATE}.txt"
# Clean old backups
find "$BACKUP_DIR" -name "*.gz" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "*.txt" -mtime +$RETENTION_DAYS -delete
echo "Backup completed at $(date)" Make executable and schedule:
chmod +x ~/linkwarden/backup.sh
# Daily at 2 AM
crontab -e
# Add: 0 2 * * * /home/user/linkwarden/backup.sh >> /var/log/linkwarden-backup.log 2>&1 Disaster Recovery
# Stop Linkwarden
docker compose down
# Restore database
gunzip < /mnt/backups/linkwarden/db-YYYYMMDD.sql.gz | docker exec -i linkwarden-db psql -U linkwarden -d linkwarden
# Restore MinIO
docker run --rm -v linkwarden_minio_data:/dest -v /mnt/backups/linkwarden:/backup alpine tar xzf /backup/minio-YYYYMMDD.tar.gz -C /dest
# Restore data
docker run --rm -v linkwarden_linkwarden_data:/dest -v /mnt/backups/linkwarden:/backup alpine tar xzf /backup/data-YYYYMMDD.tar.gz -C /dest
# Restore config
cp /mnt/backups/linkwarden/env-YYYYMMDD.txt .env
# Restart
docker compose up -d
docker compose logs -f Import from Other Services
From Browser Bookmarks
1. Export bookmarks from browser:
- Chrome: Bookmarks → Bookmark Manager → ⋮ → Export
- Firefox: Bookmarks → Show All → Import and Backup → Export
- Safari: File → Export Bookmarks
2. Linkwarden:
Settings → Import → Upload HTML file
3. Map to collections:
- Browser folder → Linkwarden collection
- Preserve folder structure
4. Import options:
- Archive all: Yes (takes time)
- Skip duplicates: Yes
- Import tags: Yes From Raindrop.io
1. Export from Raindrop:
Settings → Data → Export
2. Download CSV/JSON export
3. Import to Linkwarden:
Settings → Import → Raindrop.io format
Upload file
4. Features preserved:
- Collections
- Tags
- Descriptions
- Created dates From Pocket
1. Export from Pocket:
https://getpocket.com/export
2. Download HTML file
3. Import to Linkwarden:
Settings → Import → HTML format
4. Archive automatically (optional) Troubleshooting Common Issues
Issue: Archives failing
# Check Chromium process
docker exec linkwarden ps aux | grep chromium
# Check disk space
df -h
# Increase timeout in .env:
BROWSER_TIMEOUT=120000
# Restart
docker compose restart linkwarden Issue: Slow archiving
# Check resource usage
docker stats linkwarden
# Reduce concurrent archives in .env:
MAX_CONCURRENT_ARCHIVES=2
# Increase resources if needed
# Edit docker-compose.yml:
deploy:
resources:
limits:
cpus: '2'
memory: 4G Issue: MinIO connection errors
# Check MinIO logs
docker compose logs linkwarden-minio
# Verify bucket exists
docker exec linkwarden-minio mc ls local
# Create bucket if missing
docker exec linkwarden-minio mc mb local/linkwarden Issue: Search not working
# Rebuild search index
docker exec linkwarden npm run reindex
# Check PostgreSQL
docker exec linkwarden-db psql -U linkwarden -c "SELECT COUNT(*) FROM bookmarks;" Next Steps and Best Practices
Immediate Next Steps
Import existing bookmarks
- Export from current browser
- Import to Linkwarden
- Organize into collections
- Let archiving complete
Install browser extension
- On all devices
- Configure server URL
- Test quick save
- Set up keyboard shortcuts
Create organizational structure
- Main collections
- Tag hierarchy
- Saved searches
- Public/private separation
Set up backup routine
- Test backup script
- Verify restoration
- Schedule automated backups
- Store off-site copy
Best Practices
Bookmark management:
- Archive important pages immediately
- Tag comprehensively from the start
- Regular cleanup of old bookmarks
- Use collections for major topics
- Descriptive titles and notes
Performance:
- Archive selectively (not everything needs PDF)
- Clean old archives periodically
- Monitor disk usage
- Optimize database monthly
Team usage:
- Clear collection naming
- Consistent tagging conventions
- Regular team review sessions
- Remove duplicates
Conclusion
Linkwarden transforms ephemeral web content into a permanent, searchable archive. With powerful preservation features, collaborative tools, and complete self-hosting, it’s the ultimate solution for serious bookmark management.
Key Achievements After This Guide:
- Self-hosted bookmark manager
- Automatic page archiving
- Browser extensions installed
- Full-text search enabled
- Collaborative collections
- Backup automation
Success Metrics (First Month):
- 100+ bookmarks archived
- Browser extension actively used
- Collections organized
- Team members collaborating
- Backups running automatically
- Zero broken links in archive
Start by importing your current bookmarks, archive your reading list, and discover how much more valuable bookmarks become when they’re preserved forever!
Welcome to permanent link preservation!
Document Version: 1.0 Last Updated: December 2025 Word Count: ~5,700 words Linkwarden Version: 2.0+ (latest stable)