added ansible script

This commit is contained in:
liph
2026-02-16 23:40:30 +01:00
parent e6e444fff7
commit 8925d9677e
86 changed files with 15476 additions and 1911 deletions

View File

@@ -9,16 +9,16 @@ postpone = Drafts
folders-sort = INBOX,Draftr,Sent,Archive,Spam,Trash
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
[phil_notmuch]
source = notmuch://~/Mail/phil
query-map = ~/.config/aerc/notmuch-queries.conf
# outgoing = smtp://phil%40liphlink.xyz@mail.liphlink.xyz:587
# outgoing-cred-cmd = pass show mail/phil@liphlink.xyz
from = Phil <phil@liphlink.xyz>
default = Inbox
# copy-to = Sent
postpone = Drafts
aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
# [phil_notmuch]
# source = notmuch://~/Mail/phil
# query-map = ~/.config/aerc/notmuch-queries.conf
# # outgoing = smtp://phil%40liphlink.xyz@mail.liphlink.xyz:587
# # outgoing-cred-cmd = pass show mail/phil@liphlink.xyz
# from = Phil <phil@liphlink.xyz>
# default = Inbox
# # copy-to = Sent
# postpone = Drafts
# aliases = pw@liphlink.xyz,p.waibel@liphlink.xyz,philipp.waibel@liphlink.xyz
[spam_live]
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
@@ -31,15 +31,15 @@ postpone = Drafts
folders-sort = INBOX,Drafts,Sent,Archive,Spam,Trash
aliases = blue@liphlink.xyz,red@liphlink.xyz
[spam_notmuch]
source = notmuch://~/Mail/spam
query-map = ~/.config/aerc/notmuch-queries.conf
outgoing = smtp://spam%40liphlink.xyz@mail.liphlink.xyz:587
outgoing-cred-cmd = pass show mail/spam@liphlink.xyz
from = Liph <spam@liphlink.xyz>
copy-to = Sent
default = Inbox
aliases = blue@liphlink.xyz,red@liphlink.xyz
# [spam_notmuch]
# source = notmuch://~/Mail/spam
# query-map = ~/.config/aerc/notmuch-queries.conf
# outgoing = smtp://spam%40liphlink.xyz@mail.liphlink.xyz:587
# outgoing-cred-cmd = pass show mail/spam@liphlink.xyz
# from = Liph <spam@liphlink.xyz>
# copy-to = Sent
# default = Inbox
# aliases = blue@liphlink.xyz,red@liphlink.xyz
# [proton]
# source = imap+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1144

View File

@@ -98,7 +98,7 @@ index-format=notmuch://~/.local/share/mail
#index-columns=flags:4,name<20%,subject,date>=
border-char-vertical="│"
border-char-horizontal="─"
# styleset-name=rose-pine
styleset-name=rose-pine
#
# Each name in index-columns must have a corresponding column-$name setting.

42
ansible/.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# Ansible
*.retry
.vault_pass*
/tmp/
.ansible/
# Generated inventory (regenerate with setup.sh)
inventory/hosts.yml
inventory/group_vars/all/vars.yml
# Keep vault.yml encrypted (DO NOT ignore it - it should be committed encrypted)
# inventory/group_vars/all/vault.yml
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# Virtual environments
venv/
env/
ENV/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Temporary files
*.tmp
*.bak

259
ansible/AGENTS.md Normal file
View File

@@ -0,0 +1,259 @@
# Agent Guidelines for Ansible Infrastructure Repository
This repository contains Docker Compose infrastructure configurations for deploying self-hosted productivity services. This document provides guidelines for AI coding agents working in this repository.
## Project Overview
**Type:** Docker Compose Infrastructure Project
**Primary Technology:** Docker, Docker Compose
**Purpose:** Deployment configurations for self-hosted productivity stack including Nextcloud, OnlyOffice, Excalidraw, and Obsidian
## Repository Structure
```
/home/liph/programming/ansible/
└── nextcloud/
├── docker-compose.yml # Main orchestration file (5 services)
└── .env # Environment variables
```
## Commands
### Docker Compose Operations
```bash
# Navigate to service directory
cd nextcloud/
# Start all services
docker compose up -d
# Start specific service
docker compose up -d <service-name>
# Stop all services
docker compose down
# Stop and remove volumes (DESTRUCTIVE)
docker compose down -v
# View logs for all services
docker compose logs -f
# View logs for specific service
docker compose logs -f <service-name>
# Restart a service
docker compose restart <service-name>
# Rebuild and restart service
docker compose up -d --build <service-name>
# Validate docker-compose.yml syntax
docker compose config
# List running services
docker compose ps
```
### Service Names
- `excalidraw` - Drawing/whiteboard tool (port 3009)
- `next-db` - PostgreSQL 18 database
- `next` - Nextcloud main application (port 8808)
- `onlyoffice` - Document server (port 8000)
- `obsidian` - Note-taking app (ports 3004-3005)
### Testing
No automated tests exist. Manual testing workflow:
1. Validate syntax: `docker compose config`
2. Start services: `docker compose up -d`
3. Check health: `docker compose ps`
4. Review logs: `docker compose logs`
5. Test endpoints: `curl localhost:8808` (Nextcloud), etc.
## Code Style Guidelines
### YAML Formatting (docker-compose.yml)
**Indentation:**
- Use 2 spaces for indentation (NO tabs)
- Maintain consistent indentation levels
**Structure:**
```yaml
services:
service-name:
image: docker.io/image:tag
container_name: container-name
depends_on:
- dependency
ports:
- "host:container"
environment:
- KEY=value
- KEY=${ENV_VAR}
volumes:
- volume_name:/container/path
- ./host/path:/container/path
restart: unless-stopped
networks:
- network_name
```
**Naming Conventions:**
- Container names: Use kebab-case (e.g., `next-db`, `next-redis`)
- Service names: Use snake_case or kebab-case consistently
- Volume names: Use snake_case (e.g., `nextcloud_data`, `pg_data`)
- Network names: Use snake_case with project prefix (e.g., `nextcloud_network`)
**Environment Variables:**
- Reference .env variables using `${VAR_NAME}` syntax
- Never hardcode sensitive data (passwords, secrets) in docker-compose.yml
- Use explicit environment variable declarations with `-` prefix
**Image Specifications:**
- Always use fully qualified image names: `docker.io/image:tag`
- Pin specific versions for production (avoid `:latest` in production)
- Current exception: development environment uses `:latest` tags
**Volumes:**
- Named volumes for persistent data (defined in volumes section)
- Bind mounts for configuration files using `./relative/path`
- Always specify volume mount destination path
- CRITICAL: Never leave empty volume definitions (e.g., `- :/path`)
**Comments:**
- Use `#` for comments
- Add comments above service definitions to describe purpose
- Comment out optional services with `# ` prefix on each line
### Environment Files (.env)
**Format:**
```bash
# Database Configuration
DB_PASSWORD=secure_password_here
DB_USERNAME=nextcloud
DB_DATABASE_NAME=nextcloud
DB_HOST=next-db
# User/Group IDs
PUID=33
PGID=1000
```
**Rules:**
- One variable per line: `KEY=value`
- No spaces around `=`
- No quotes needed for values
- Group related variables with comment headers
- Never commit sensitive values (use placeholders or .env.example)
- All variables must have values (no empty assignments)
### Security Guidelines
1. **Secrets Management:**
- Never hardcode passwords in docker-compose.yml
- Use .env file for credentials (ensure .env is in .gitignore)
- Consider Docker secrets for sensitive production data
- Rotate default passwords immediately
2. **Current Security Issues to Fix:**
- Line 53 in docker-compose.yml: Remove hardcoded admin password
- .env file: All database credentials are empty
- Consider enabling JWT for OnlyOffice (currently disabled)
3. **Network Security:**
- Use isolated Docker networks for service communication
- Only expose necessary ports to host
- Consider reverse proxy (nginx/traefik) for HTTPS termination
### Error Handling
**Validation Steps Before Committing:**
1. Run `docker compose config` to validate YAML syntax
2. Ensure all volume mount paths are complete
3. Verify all environment variables are defined in .env
4. Check for hardcoded secrets
5. Confirm port conflicts with `docker compose ps`
**Common Issues:**
- Empty volume definitions: Always specify source and destination
- Missing environment variables: Define all referenced vars in .env
- Port conflicts: Use `docker ps` to check existing port bindings
- Network errors: Ensure services on same network can communicate
### Configuration Management
**Volume Mapping Patterns:**
```yaml
# Named volume (recommended for data)
volumes:
- nextcloud_data:/var/www/html
# Bind mount with SELinux label (for config)
volumes:
- ./config:/var/www/html/config:Z
# Bind mount with shared label
volumes:
- ./vault:/vault:z
```
**Restart Policies:**
- Use `restart: unless-stopped` for production services
- Avoid `restart: always` (harder to stop deliberately)
- Use `restart: on-failure` for development/debugging
### Database Configuration
**PostgreSQL Environment Variables:**
- `POSTGRES_DB` - Database name
- `POSTGRES_USER` - Database user
- `POSTGRES_PASSWORD` - Database password
**Nextcloud Database Connection:**
- `POSTGRES_HOST` - Hostname (service name: `next-db`)
- Must match database service configuration
### Best Practices
1. **Service Dependencies:**
- Use `depends_on` to define startup order
- Note: `depends_on` doesn't wait for "ready" state
2. **Resource Limits:**
- Consider adding memory/CPU limits for production
- Current config uses `mem_swappiness: -1` for Nextcloud
3. **Timezone Configuration:**
- Set `TZ` environment variable for correct timestamps
- Current: `TZ=Europe/Zurich` (Excalidraw), `TZ=Etc/UTC` (Obsidian)
4. **Data Persistence:**
- Always use named volumes for important data
- Define volumes in top-level `volumes:` section
- Backup volume data regularly
5. **Making Changes:**
- Test changes in development before production
- Use `docker compose config` to validate
- Review diff carefully before applying
- Document breaking changes
## Development Workflow
1. Make changes to docker-compose.yml or .env
2. Validate: `docker compose config`
3. Apply changes: `docker compose up -d`
4. Verify: `docker compose ps && docker compose logs`
5. Test affected services manually
6. Document changes in commit message
## Critical Fixes Needed
- [ ] Complete empty volume mount paths (lines 21, 55-57, 74, 89-90)
- [ ] Remove hardcoded admin password (line 53)
- [ ] Populate database credentials in .env file
- [ ] Add .gitignore to exclude .env from version control
- [ ] Consider adding docker-compose.override.yml for local development

426
ansible/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,426 @@
# Network Architecture
Visual overview of how all services connect and communicate.
## 🌐 Network Flow Diagram
```
INTERNET
│ DNS A Records
│ (cloud.yourdomain.com → SERVER_IP)
┌───────────────┐
│ Firewall (UFW)│
│ Ports: 80 │
│ 443 │
│ 41641 │
└───────┬───────┘
┌────────────────┼────────────────┐
│ │ │
Port 80/443 Port 41641 Port 22
│ │ │
↓ ↓ ↓
┌──────────────┐ ┌─────────────┐ ┌──────────┐
│ CADDY │ │ TAILSCALE │ │ SSH │
│ Reverse Proxy│ │ VPN │ │ Access │
└──────┬───────┘ └─────┬───────┘ └──────────┘
│ │
│ │
┌──────────┴────────┐ │
│ │ │
↓ ↓ ↓
┌─────────┐ ┌─────────────────┐
│ PUBLIC │ │ TAILSCALE-ONLY │
│SERVICES │ │ SERVICES │
└─────────┘ └─────────────────┘
```
---
## 🔐 Access Matrix
| Service | Public URL | Tailscale URL | Direct Access |
|---------|-----------|---------------|---------------|
| **Nextcloud** | ✅ https://cloud.domain.com | ✅ Via Tailscale IP | ❌ No |
| **OnlyOffice** | ✅ https://office.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Excalidraw** | ✅ https://draw.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Obsidian** | ✅ https://notes.domain.com | ✅ Via Tailscale IP | ❌ No |
| **Homarr** | ❌ 403 Forbidden | ✅ https://home.domain.com | ❌ No |
| **Dockhand** | ❌ 403 Forbidden | ✅ https://manage.domain.com | ❌ No |
| **Uptime Kuma** | ❌ 403 Forbidden | ✅ https://uptime.domain.com | ❌ No |
| **PostgreSQL** | ❌ No | ❌ No | ✅ Docker network only |
| **Redis** | ❌ No | ❌ No | ✅ Docker network only |
| **Caddy** | ❌ No (serves others) | ❌ No | ✅ Docker network only |
| **Watchtower** | ❌ No | ❌ No | ✅ Docker network only |
---
## 🐳 Docker Network Topology
```
┌────────────────────────────────────────────────────────────────┐
│ nextcloud_network (bridge) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ Redis │ │Nextcloud │ │OnlyOffice│ │
│ │ :5432 │ │ :6379 │ │ :80 │ │ :80 │ │
│ └──────────┘ └──────────┘ └────┬─────┘ └──────────┘ │
│ ↑ ↑ │ │
│ │ │ │ │
│ └──────────────┴─────────────┘ │
│ │ │
│ ┌──────────┐ ┌────┴─────┐ ┌──────────┐ ┌──────────┐ │
│ │Excalidraw│ │ Caddy │ │ Obsidian │ │ Homarr │ │
│ │ :80 │ │80→:80 │ │ :3000 │ │ :7575 │ │
│ └──────────┘ │443→:443 │ └──────────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌──────────┐ │ │ ┌──────────┐ ↓ │
│ │ Dockhand │ │ │ │ Uptime │ /var/run/ │
│ │ :3000 │ │ │ │ Kuma │ docker.sock │
│ └──────────┘ └──────────┘ │ :3001 │ (read-only) │
│ └──────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Watchtower (monitors all) │ │
│ │ Accesses: /var/run/docker.sock │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
│ │
│ Exposed Ports │ Internal
│ │ Communication
↓ ↓
Host: 80, 443 Container-to-Container
(Caddy only) via Docker DNS
```
---
## 🔄 Request Flow Examples
### Example 1: User Accesses Nextcloud
```
User Browser
│ HTTPS GET https://cloud.yourdomain.com
Internet
Firewall (Port 443)
Caddy Container (:443)
│ Validates SSL certificate
│ Checks Caddyfile rules
│ Proxies to backend
Nextcloud Container (:80)
│ Checks trusted domains
│ Queries PostgreSQL
│ Checks Redis cache
PostgreSQL + Redis
│ Returns data
Nextcloud → Caddy → User
```
### Example 2: User Accesses Homarr (via Tailscale)
```
User Browser (on Tailscale)
│ HTTPS GET https://home.yourdomain.com
Internet → DNS Resolution
Firewall (Port 443)
Caddy Container
│ Checks client IP: 100.64.x.x (Tailscale range)
│ ✅ Match! Proxy to backend
Homarr Container (:7575)
│ Reads Docker socket
│ Shows container status
Returns dashboard → Caddy → User
```
### Example 3: User Accesses Homarr (NOT on Tailscale)
```
User Browser (public internet)
│ HTTPS GET https://home.yourdomain.com
Internet → DNS Resolution
Firewall (Port 443)
Caddy Container
│ Checks client IP: NOT in 100.64.0.0/10
│ ❌ No match! Return 403 Forbidden
403 Access Denied
```
### Example 4: Nextcloud Connects to Database
```
Nextcloud Container
│ Docker DNS lookup: next-db
│ Resolves to: 172.20.0.x
PostgreSQL Container (next-db:5432)
│ Internal Docker network
│ No external exposure
Returns query results
```
---
## 📊 Data Flow Diagram
### File Upload to Nextcloud
```
User → Caddy → Nextcloud → Storage Volume
├→ PostgreSQL (metadata)
└→ Redis (cache)
```
### Document Editing with OnlyOffice
```
User → Caddy → Nextcloud → Caddy → OnlyOffice
│ │
↓ ↓
PostgreSQL Document
(metadata) Processing
│ │
↓ ↓
Nextcloud ←───────── OnlyOffice
Storage Volume
```
### Watchtower Auto-Update
```
Watchtower (cron: every 24h)
│ Check Docker Hub for new images
New image available?
├─ NO → Sleep
├─ YES → Check container labels
├─ "enable=false" → Skip
├─ "enable=true" → Pull & Update
└─ "monitor-only=true" → Notify
```
---
## 🔐 Security Boundaries
```
┌─────────────────────────────────────────────────────────────┐
│ INTERNET (Untrusted) │
└────────────────────────┬────────────────────────────────────┘
┌────────▼────────┐
│ UFW Firewall │ ← Layer 1: Host Firewall
│ 22, 80, 443 │
└────────┬────────┘
┌────────▼────────┐
│ fail2ban │ ← Layer 2: Intrusion Detection
│ SSH Protection │
└────────┬────────┘
┌────────▼────────┐
│ Caddy (HTTPS) │ ← Layer 3: TLS Termination
│ SSL Validation │
└────────┬────────┘
┌────────────────┼────────────────┐
│ │ │
┌───▼────┐ ┌────▼────┐ ┌─────▼─────┐
│ Public │ │Tailscale│ │ Docker │ ← Layer 4: Network
│Services│ │ Check │ │ Network │ Isolation
└────────┘ └─────────┘ └───────────┘
┌────▼────┐
│ Admin │ ← Layer 5: Admin Access
│ Services│ (Tailscale VPN only)
└─────────┘
```
---
## 🗄️ Data Persistence
### Docker Volumes
```
Host Filesystem
├── /var/lib/docker/volumes/
│ ├── nextcloud_pg_data/ ← PostgreSQL database
│ ├── nextcloud_redis_data/ ← Redis cache
│ ├── nextcloud_nextcloud_data/ ← Nextcloud files
│ ├── nextcloud_nextcloud_apps/ ← Nextcloud apps
│ ├── nextcloud_onlyoffice_data/ ← OnlyOffice docs
│ ├── nextcloud_caddy_data/ ← SSL certificates
│ ├── nextcloud_caddy_config/ ← Caddy config
│ ├── nextcloud_dockhand_data/ ← Dockhand settings
│ └── nextcloud_uptime_kuma_data/ ← Uptime Kuma data
└── /opt/nextcloud-stack/
├── configs/
│ ├── caddy/Caddyfile
│ └── nextcloud/
├── data/
│ ├── homarr/
│ └── obsidian/
└── backups/
├── database/
└── volumes/
```
---
## 🔄 Update Mechanism
### Watchtower Label-Based Control
```yaml
# Auto-update allowed
labels:
- "com.centurylinklabs.watchtower.enable=true"
→ Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
# Monitor only (notify, no update)
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
→ OnlyOffice
# Never auto-update (manual only)
labels:
- "com.centurylinklabs.watchtower.enable=false"
→ Nextcloud, PostgreSQL, Redis
```
---
## 📡 Monitoring Architecture
```
┌────────────────────────────────────────────────────────┐
│ Uptime Kuma │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ HTTP Monitors (every 60s) │ │
│ │ • https://cloud.yourdomain.com/status.php │ │
│ │ • https://office.yourdomain.com/health │ │
│ │ • https://draw.yourdomain.com │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ TCP Port Monitors │ │
│ │ • next-db:5432 (PostgreSQL) │ │
│ │ • next-redis:6379 (Redis) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ SSL Certificate Monitor │ │
│ │ • Expiry check (30-day warning) │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ Notifications │ │
│ │ • Email alerts │ │
│ │ • Slack/Discord webhooks (optional) │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
```
---
## 🎯 Service Dependencies
```
Nextcloud
├─ requires → PostgreSQL (healthy)
├─ requires → Redis (healthy)
└─ requires → Caddy (for external access)
OnlyOffice
└─ requires → Caddy (for external access)
Homarr, Dockhand
├─ requires → Docker Socket (read-only)
└─ requires → Caddy (for Tailscale access)
Watchtower
└─ requires → Docker Socket (read-write)
All Services
└─ network → nextcloud_network
```
---
## 🌟 Architecture Benefits
### High Availability Features
- **Health checks**: Containers auto-restart on failure
- **Database connection pooling**: Redis caching reduces DB load
- **SSL auto-renewal**: Caddy handles certificate lifecycle
### Security Layers
1. **Network**: UFW firewall + fail2ban
2. **Transport**: TLS/SSL via Caddy
3. **Access**: Tailscale VPN for admin interfaces
4. **Secrets**: Ansible Vault encryption
5. **Isolation**: Docker network segmentation
### Scalability Points
- **Database**: Can migrate to external PostgreSQL
- **Cache**: Redis can be scaled/clustered
- **Storage**: Volumes can be moved to network storage
- **Reverse Proxy**: Caddy can be load-balanced
---
**This architecture provides a secure, maintainable, and production-ready self-hosted cloud platform.**

420
ansible/BACKUP_RESTORE.md Normal file
View File

@@ -0,0 +1,420 @@
# Backup and Restore Guide
Complete guide for backing up and restoring your Nextcloud Stack.
## Automated Backups
### What Gets Backed Up
- **Daily (3:00 AM)**:
- PostgreSQL database dump
- **Weekly (Sundays, 3:00 AM)**:
- Nextcloud data volume
- Configuration files
- **Retention**: 30 days
### Backup Locations
```
/opt/nextcloud-stack/backups/
├── database/
│ └── nextcloud_db_YYYYMMDD_HHMMSS.sql.gz
├── volumes/
│ ├── nextcloud_data_YYYYMMDD_HHMMSS.tar.gz
│ └── configs_YYYYMMDD_HHMMSS.tar.gz
└── backup.log
```
### Manual Backup
```bash
ssh user@server
/opt/nextcloud-stack/backup.sh
```
### Check Backup Status
```bash
# View backup log
tail -f /opt/nextcloud-stack/backups/backup.log
# List backups
ls -lh /opt/nextcloud-stack/backups/database/
ls -lh /opt/nextcloud-stack/backups/volumes/
# Check disk usage
du -sh /opt/nextcloud-stack/backups/
```
---
## Restore Procedures
### 1. Restore Database Only
**When to use:**
- Database corruption
- Accidental data deletion
- Need to revert to earlier state
**Steps:**
```bash
# 1. Stop Nextcloud (keep database running)
cd /opt/nextcloud-stack
docker compose stop next
# 2. List available backups
ls -lh backups/database/
# 3. Choose backup to restore
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
# 4. Drop existing database (CAUTION!)
docker exec next-db psql -U nextcloud -c "DROP DATABASE nextcloud;"
docker exec next-db psql -U nextcloud -c "CREATE DATABASE nextcloud;"
# 5. Restore from backup
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
# 6. Restart Nextcloud
docker compose start next
# 7. Run Nextcloud upgrade (if needed)
docker exec -u www-data next php occ upgrade
# 8. Disable maintenance mode
docker exec -u www-data next php occ maintenance:mode --off
```
### 2. Restore Data Volumes
**When to use:**
- Lost files
- Corrupted data directory
- Complete system failure
**Steps:**
```bash
# 1. Stop all services
cd /opt/nextcloud-stack
docker compose down
# 2. List available backups
ls -lh backups/volumes/
# 3. Choose backup
BACKUP_FILE="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
# 4. Restore volume
# Note: This requires accessing the Docker volume directory
sudo tar -xzf $BACKUP_FILE -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 5. Restore configs (optional)
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
# 6. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 7. Start services
docker compose up -d
# 8. Run Nextcloud file scan
docker exec -u www-data next php occ files:scan --all
```
### 3. Complete System Restore
**When to use:**
- Disaster recovery
- Migration to new server
- Complete system failure
**Steps:**
```bash
# 1. On new server, install base system
# Run Ansible playbooks 01-04 (up to Tailscale)
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
ansible-playbook playbooks/02-system-setup.yml --ask-vault-pass
ansible-playbook playbooks/03-docker-setup.yml --ask-vault-pass
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
# 2. Create deployment directory structure
ssh user@new-server
sudo mkdir -p /opt/nextcloud-stack/backups/{database,volumes}
# 3. Copy backups from old server to new server
# On your local machine:
scp -r old-server:/opt/nextcloud-stack/backups/* user@new-server:/opt/nextcloud-stack/backups/
# 4. Deploy stack (creates volumes and containers)
ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
# 5. Stop services on new server
ssh user@new-server
cd /opt/nextcloud-stack
docker compose down
# 6. Restore database
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
docker compose up -d next-db next-redis
sleep 10
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud
# 7. Restore data volumes
VOLUME_BACKUP="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
sudo tar -xzf $VOLUME_BACKUP -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 8. Restore configs
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/
# 9. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/
# 10. Start all services
docker compose up -d
# 11. Run Nextcloud maintenance
docker exec -u www-data next php occ maintenance:mode --off
docker exec -u www-data next php occ files:scan --all
docker exec -u www-data next php occ db:add-missing-indices
# 12. Continue with remaining playbooks
exit
ansible-playbook playbooks/06-configure-caddy.yml --ask-vault-pass
ansible-playbook playbooks/07-setup-backups.yml --ask-vault-pass
ansible-playbook playbooks/08-post-deployment.yml --ask-vault-pass
```
---
## Off-Site Backups with rclone
### Initial Setup
```bash
ssh user@server
# Configure rclone
rclone config
# Example: Setup Backblaze B2
# Follow prompts:
# - Choose: New remote
# - Name: b2backup
# - Storage: Backblaze B2
# - Enter account ID and application key
```
### Sync Backups to Remote
```bash
# Test sync (dry run)
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups --dry-run
# Actual sync
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups
```
### Automated Off-Site Backups
Add to `/opt/nextcloud-stack/backup.sh` (after the cleanup section):
```bash
# Off-site backup (if rclone configured)
if command -v rclone &> /dev/null && rclone listremotes | grep -q "b2backup"; then
log "Syncing backups to off-site storage..."
rclone sync $BACKUP_DIR b2backup:nextcloud-backups --log-file=$LOG_FILE
log "Off-site sync completed"
fi
```
### Restore from Off-Site Backup
```bash
# List remote backups
rclone ls b2backup:nextcloud-backups/database/
# Download specific backup
rclone copy b2backup:nextcloud-backups/database/nextcloud_db_20260216_030000.sql.gz /opt/nextcloud-stack/backups/database/
# Download all backups
rclone sync b2backup:nextcloud-backups /opt/nextcloud-stack/backups/
```
---
## Backup Verification
### Test Database Backup Integrity
```bash
# 1. Extract backup
gunzip -c backups/database/nextcloud_db_20260216_030000.sql.gz > /tmp/test_restore.sql
# 2. Check for errors
grep -i "error" /tmp/test_restore.sql
# 3. Verify size (should be reasonable)
ls -lh /tmp/test_restore.sql
# 4. Cleanup
rm /tmp/test_restore.sql
```
### Test Volume Backup Integrity
```bash
# Test tar archive
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz | head -20
# Check for errors
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz > /dev/null && echo "Archive OK"
```
---
## Migration to New Server
**Complete migration guide:**
1. **Prepare new server:**
- Same or newer Ubuntu version
- Run Ansible setup playbooks (01-04)
2. **Backup old server:**
```bash
/opt/nextcloud-stack/backup.sh
```
3. **Copy backups to new server:**
```bash
rsync -avz old-server:/opt/nextcloud-stack/backups/ new-server:/opt/nextcloud-stack/backups/
```
4. **Update DNS:**
- Point all domains to new server IP
- Wait for propagation
5. **Restore on new server:**
- Follow "Complete System Restore" steps above
6. **Verify:**
- Test all services
- Check Nextcloud functionality
- Verify SSL certificates
7. **Decommission old server:**
- Only after confirming new server works
- Keep final backup from old server
---
## Backup Best Practices
### DO:
- ✅ Test restores regularly (monthly)
- ✅ Keep backups off-site (rclone to cloud storage)
- ✅ Verify backup integrity
- ✅ Monitor backup log for errors
- ✅ Document restore procedures
- ✅ Keep multiple backup generations
### DON'T:
- ❌ Store backups only on same server
- ❌ Assume backups work without testing
- ❌ Delete old backups without verification
- ❌ Skip database backups
- ❌ Ignore backup failure notifications
---
## Backup Schedule Customization
Edit `/opt/nextcloud-stack/backup.sh` to change:
- Backup frequency
- Retention period
- What gets backed up
- Compression settings
Edit cron job:
```bash
sudo crontab -e
# Change from 3:00 AM to 2:00 AM
0 2 * * * /opt/nextcloud-stack/backup.sh >> /opt/nextcloud-stack/backups/backup.log 2>&1
```
---
## Troubleshooting Backups
### Backup script fails
**Check logs:**
```bash
tail -100 /opt/nextcloud-stack/backups/backup.log
```
**Common issues:**
- Disk space full
- Docker container not running
- Permission denied
**Solutions:**
```bash
# Check disk space
df -h
# Check containers
docker ps
# Fix permissions
sudo chown -R root:root /opt/nextcloud-stack/backup.sh
sudo chmod +x /opt/nextcloud-stack/backup.sh
```
### Database backup empty
**Verify database is running:**
```bash
docker exec next-db pg_isready -U nextcloud
```
**Test manual backup:**
```bash
docker exec next-db pg_dump -U nextcloud nextcloud > /tmp/test.sql
ls -lh /tmp/test.sql
```
---
## Emergency Recovery
If all backups are lost and you need to start fresh:
```bash
# 1. Remove everything
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
cd /opt/nextcloud-stack
docker compose down -v
# 2. Redeploy from scratch
ansible-playbook playbooks/site.yml --ask-vault-pass
# 3. Reconfigure Nextcloud manually
# Login and set up users, apps, etc.
```
---
**Remember:** Backups are worthless unless you've tested restoring from them!
**Last Updated:** 2026-02-16

470
ansible/DEPLOYMENT_GUIDE.md Normal file
View File

@@ -0,0 +1,470 @@
# Quick Deployment Guide
Get your Nextcloud Stack running in under an hour!
## ⚡ Quick Start (3 Steps)
### Step 1: Configure (5-10 minutes)
```bash
./setup.sh
```
Answer the questions about your servers and preferences. This generates your encrypted configuration.
### Step 2: Deploy (30-45 minutes per server)
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
### Step 3: Access Your Services
- **Nextcloud**: `https://cloud.yourdomain.com`
- **OnlyOffice**: `https://office.yourdomain.com`
- **Excalidraw**: `https://draw.yourdomain.com`
- **Obsidian**: `https://notes.yourdomain.com`
**Management (Tailscale only)**:
- **Homarr Dashboard**: `https://home.yourdomain.com`
- **Dockhand**: `https://manage.yourdomain.com`
- **Uptime Kuma**: `https://uptime.yourdomain.com`
---
## 📋 Prerequisites Checklist
### Before You Begin
- [ ] Ubuntu 20.04+ server (LXC or VPS)
- [ ] Root or sudo access
- [ ] Minimum 100GB disk space
- [ ] Ansible 2.14+ on your control machine
- [ ] Domain name with DNS access
### DNS Configuration (CRITICAL!)
Before deployment, create these A records pointing to your server IP:
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
**Verify DNS before deployment:**
```bash
dig +short cloud.yourdomain.com
# Should return: YOUR_SERVER_IP
```
---
## 🎯 What Gets Deployed
### 12 Services
| Service | Purpose | Auto-Update |
|---------|---------|-------------|
| **Nextcloud** | File sync & collaboration | ❌ Manual |
| **PostgreSQL 18** | Database | ❌ Manual |
| **Redis 7** | Cache | ❌ Manual |
| **OnlyOffice** | Document editor | 👁️ Monitor |
| **Excalidraw** | Whiteboard | ✅ Auto |
| **Obsidian** | Notes | ✅ Auto |
| **Caddy** | Reverse proxy + SSL | ✅ Auto |
| **Homarr** | Dashboard | ✅ Auto |
| **Dockhand** | Container manager | ✅ Auto |
| **Uptime Kuma** | Monitoring | ✅ Auto |
| **Watchtower** | Auto-updater | N/A |
### Security Features
- ✅ UFW firewall configured
- ✅ Fail2ban for SSH protection
- ✅ Automatic SSL certificates (Let's Encrypt)
- ✅ Ansible Vault encrypted secrets
- ✅ Tailscale-only admin interfaces
- ✅ Automatic security updates
### Backup System
- **Daily**: Database backups (3:00 AM)
- **Weekly**: Volume backups (Sundays)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
---
## 📝 Detailed Setup Process
### 1. Install Ansible (Control Machine)
**Ubuntu/Debian:**
```bash
sudo apt update
sudo apt install ansible
```
**macOS:**
```bash
brew install ansible
```
**Verify:**
```bash
ansible --version
# Should be 2.14 or higher
```
### 2. Clone Repository
```bash
git clone <your-repo-url>
cd ansible-nextcloud-deployment
```
### 3. Run Interactive Setup
```bash
chmod +x setup.sh
./setup.sh
```
**You'll be asked for:**
1. **Server Details** (for each server):
- IP address
- Hostname
- Domain (e.g., `example.com`)
- SSH user
- SSH key path
2. **User Information**:
- Your name
- Your email (for SSL certificates)
- Timezone
3. **Admin Account**:
- Nextcloud admin username
- Nextcloud admin password
4. **Subdomain Preferences** (defaults shown):
- Nextcloud: `cloud`
- OnlyOffice: `office`
- Excalidraw: `draw`
- Obsidian: `notes`
- Homarr: `home`
- Dockhand: `manage`
- Uptime Kuma: `uptime`
5. **Tailscale** (optional):
- Auth key (or skip for manual activation)
6. **Monitoring**:
- Alert email
- Public status page (yes/no)
7. **Backups**:
- Install rclone (yes/no)
8. **Ansible Vault Password**:
- Create a password to encrypt secrets
**Output:**
- `inventory/hosts.yml` - Server inventory
- `inventory/group_vars/all/vars.yml` - Public variables
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
### 4. Test Connectivity
```bash
make ping
```
Or:
```bash
ansible all -m ping --ask-vault-pass
```
Expected output:
```
server-hostname | SUCCESS => {
"ping": "pong"
}
```
### 5. Run Deployment
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
**What happens** (30-45 minutes):
1.**Preflight Checks** (2 min)
- Validates DNS, connectivity, disk space
2. ⚙️ **System Setup** (10-15 min)
- Installs packages
- Configures firewall (UFW)
- Sets up fail2ban
3. 🐳 **Docker Setup** (5 min)
- Removes old Docker
- Installs Docker CE + Compose v2
4. 🔐 **Tailscale** (2 min)
- Installs Tailscale
- Activates if auth key provided
5. 📦 **Deploy Stack** (10-15 min)
- Templates configuration files
- Pulls Docker images
- Starts all containers
- Initializes Nextcloud
6. 🌐 **Configure Caddy** (2-5 min)
- Validates configuration
- Obtains SSL certificates
7. 💾 **Setup Backups** (2 min)
- Creates backup script
- Sets up cron jobs
8.**Post-Deployment** (5 min)
- Verifies all services
- Installs Nextcloud apps
- Creates deployment report
### 6. Access Your Services
**Immediately after deployment:**
1. **Nextcloud**: `https://cloud.yourdomain.com`
- Login with admin credentials from setup
2. **Setup Uptime Kuma** (via Tailscale):
- `https://uptime.yourdomain.com`
- Create admin account on first visit
- Add monitors for your services
3. **Configure Homarr** (via Tailscale):
- `https://home.yourdomain.com`
- Add service tiles
- Customize dashboard
4. **OnlyOffice Integration**:
- Nextcloud → Settings → Administration → Office
- Document Server: `https://office.yourdomain.com`
---
## 🔧 Post-Deployment Tasks
### Activate Tailscale (if not done automatically)
```bash
ssh user@server
sudo tailscale up
```
### Test Backups
```bash
make backup
```
Or on the server:
```bash
ssh user@server
/opt/nextcloud-stack/backup.sh
```
### Check Service Status
```bash
make status
```
Or on the server:
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose ps
```
### View Logs
```bash
make logs
```
Or on the server:
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose logs -f
```
---
## 🎓 Learning Ansible Commands
### Useful Makefile Shortcuts
```bash
make help # Show all commands
make ping # Test connectivity
make check # Run preflight checks only
make deploy # Full deployment
make deploy-dry # Test without making changes
make status # Show container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
### Direct Ansible Commands
```bash
# Run specific playbook
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
# Run on specific server
ansible-playbook playbooks/site.yml --limit server1 --ask-vault-pass
# Dry run (no changes)
ansible-playbook playbooks/site.yml --ask-vault-pass --check
# View inventory
ansible-inventory --list
# Run ad-hoc command
ansible all -m shell -a "docker ps" --ask-vault-pass
```
---
## 🚨 Troubleshooting
### DNS Not Configured
**Symptom**: Let's Encrypt fails
**Solution**: Ensure all DNS A records point to server IP
```bash
dig +short cloud.yourdomain.com
```
### LXC Container Issues
**Symptom**: Docker won't start
**Solution** (on LXC host):
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### Port Already in Use
**Symptom**: Caddy fails to start
**Solution**:
```bash
sudo systemctl stop apache2
sudo systemctl stop nginx
```
### Full troubleshooting guide:
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
---
## 📚 Additional Documentation
- [README.md](README.md) - Complete project overview
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Common issues and solutions
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup and restore procedures
- [AGENTS.md](AGENTS.md) - Development guidelines
---
## 🔒 Security Best Practices
### After Deployment
1. **Change default passwords** (if you used simple ones during testing)
2. **Enable 2FA** in Nextcloud
3. **Review firewall rules**: `sudo ufw status`
4. **Check fail2ban**: `sudo fail2ban-client status sshd`
5. **Verify SSL certificates**: Visit all your domains in browser
6. **Test backups**: Ensure they're working
7. **Setup monitoring alerts** in Uptime Kuma
### Ongoing Maintenance
- **Weekly**: Check Uptime Kuma for service health
- **Monthly**: Test backup restore
- **Quarterly**: Review security updates
- **Annually**: Rotate passwords
---
## 🆘 Getting Help
1. **Check logs:**
```bash
docker compose logs [service-name]
```
2. **Deployment report:**
```bash
cat /opt/nextcloud-stack/DEPLOYMENT.txt
```
3. **System status:**
```bash
docker compose ps
sudo systemctl status docker
```
---
## ✅ Success Checklist
After deployment, verify:
- [ ] All containers running: `docker compose ps`
- [ ] Nextcloud accessible via HTTPS
- [ ] SSL certificate valid (green padlock in browser)
- [ ] Can login to Nextcloud
- [ ] OnlyOffice integration working
- [ ] Uptime Kuma showing all services green
- [ ] Homarr dashboard accessible
- [ ] Backup script runs successfully
- [ ] Tailscale connected (if using)
---
**You're all set! Enjoy your self-hosted Nextcloud stack! 🎉**
For questions or issues, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
**Last Updated:** 2026-02-16

61
ansible/Makefile Normal file
View File

@@ -0,0 +1,61 @@
# Makefile for Nextcloud Stack Deployment
# Provides convenient shortcuts for common Ansible operations
.PHONY: help setup ping check deploy deploy-stack deploy-dry update backup logs status restart rollback clean edit-vault rekey-vault
help: ## Show this help message
@echo "Nextcloud Stack Deployment - Make Commands"
@echo ""
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
setup: ## Run interactive setup script
@./setup.sh
ping: ## Test connectivity to all servers
@ansible all -m ping --ask-vault-pass
check: ## Run preflight checks only
@ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
deploy: ## Full deployment (all playbooks)
@ansible-playbook playbooks/site.yml --ask-vault-pass
deploy-stack: ## Deploy stack only (skip system setup)
@ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass
deploy-dry: ## Dry run (check mode)
@ansible-playbook playbooks/site.yml --ask-vault-pass --check
update: ## Update Docker images (safe services only)
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose pull && docker compose up -d" --ask-vault-pass
backup: ## Run manual backup on all servers
@ansible all -m shell -a "/opt/nextcloud-stack/backup.sh" --ask-vault-pass
logs: ## Tail logs from all containers
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose logs -f --tail=100" --ask-vault-pass
status: ## Show container status
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose ps" --ask-vault-pass
restart: ## Restart all containers
@ansible all -m shell -a "cd /opt/nextcloud-stack && docker compose restart" --ask-vault-pass
rollback: ## Emergency rollback
@ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
clean: ## Remove generated inventory (keeps vault)
@rm -f inventory/hosts.yml inventory/group_vars/all/vars.yml
@echo "Cleaned generated files (vault.yml preserved)"
edit-vault: ## Edit encrypted vault
@ansible-vault edit inventory/group_vars/all/vault.yml
rekey-vault: ## Change vault password
@ansible-vault rekey inventory/group_vars/all/vault.yml
inventory: ## Show inventory configuration
@ansible-inventory --list
graph: ## Show inventory graph
@ansible-inventory --graph

560
ansible/PROJECT_SUMMARY.md Normal file
View File

@@ -0,0 +1,560 @@
# Nextcloud Stack - Ansible Deployment Project
## 🎉 Project Complete!
This Ansible automation project provides complete, one-command deployment of a self-hosted Nextcloud productivity stack to Ubuntu-based LXC/VPS servers.
---
## 📦 What's Included
### Core Files
```
ansible-nextcloud-deployment/
├── setup.sh # ⭐ Interactive configuration wizard
├── Makefile # Convenient command shortcuts
├── ansible.cfg # Ansible configuration
├── .gitignore # Git ignore rules
├── inventory/ # Generated by setup.sh
│ ├── hosts.yml # Server inventory (generated)
│ └── group_vars/all/
│ ├── vars.yml # Public variables (generated)
│ └── vault.yml # Encrypted secrets (generated)
├── playbooks/ # ⭐ Deployment playbooks
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # DNS, connectivity validation
│ ├── 02-system-setup.yml # Packages, firewall, security
│ ├── 03-docker-setup.yml # Docker CE installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Docker Compose deployment
│ ├── 06-configure-caddy.yml # Reverse proxy + SSL
│ ├── 07-setup-backups.yml # Backup automation
│ ├── 08-post-deployment.yml # Final verification
│ └── 99-rollback.yml # Emergency rollback
├── roles/ # ⭐ Ansible roles
│ ├── nextcloud_stack/
│ │ └── templates/
│ │ ├── docker-compose.yml.j2 # Complete stack definition
│ │ └── env.j2 # Environment variables
│ └── caddy/
│ └── templates/
│ └── Caddyfile.j2 # Reverse proxy config
└── docs/ # ⭐ Documentation
├── README.md # Project overview
├── DEPLOYMENT_GUIDE.md # Quick start guide
├── TROUBLESHOOTING.md # Common issues & solutions
└── BACKUP_RESTORE.md # Backup procedures
```
### Stack Components (12 Services)
**Public Services** (via HTTPS):
- Nextcloud (file sync & collaboration)
- OnlyOffice (document editor)
- Excalidraw (whiteboard)
- Obsidian (note-taking)
**Infrastructure**:
- PostgreSQL 18 (database)
- Redis 7 (cache)
- Caddy (reverse proxy + automatic SSL)
**Management** (Tailscale-only):
- Homarr (dashboard)
- Dockhand (container manager)
- Uptime Kuma (monitoring)
**Automation**:
- Watchtower (auto-updates with safety exclusions)
---
## 🚀 Quick Start
### 1. Configure
```bash
./setup.sh
```
Interactive wizard collects all configuration:
- Server details (IP, domain, SSH)
- User information
- Admin credentials
- Subdomain preferences
- Tailscale auth key (optional)
- Monitoring settings
**Generates:**
- `inventory/hosts.yml` - Server inventory
- `inventory/group_vars/all/vars.yml` - Public config
- `inventory/group_vars/all/vault.yml` - Encrypted secrets
### 2. Deploy
```bash
make deploy
```
Or:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
**Duration:** 30-45 minutes per server
### 3. Access
- Nextcloud: `https://cloud.yourdomain.com`
- Homarr: `https://home.yourdomain.com` (Tailscale)
- Uptime Kuma: `https://uptime.yourdomain.com` (Tailscale)
---
## ✨ Key Features
### 🔒 Security
- **Firewall**: UFW configured (only SSH, HTTP, HTTPS, Tailscale open)
- **Fail2ban**: SSH brute-force protection (5 failures = 10 min ban)
- **Automatic Security Updates**: Unattended upgrades enabled
- **Secret Management**: Ansible Vault encryption for all credentials
- **Access Control**: Management UIs restricted to Tailscale network
- **SSL/TLS**: Automatic Let's Encrypt certificates via Caddy
- **Container Isolation**: Dedicated Docker network
### 💾 Backups
- **Daily**: PostgreSQL database dumps (3:00 AM)
- **Weekly**: Full volume backups (Sundays, 3:00 AM)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
- **Off-site Ready**: rclone pre-installed
- **Automated**: Cron jobs configured
- **Tested**: Documented restore procedures
### 🔄 Updates
**Automated (Watchtower)**:
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
**Monitor Only**:
- OnlyOffice (notifications, manual update)
**Manual Required**:
- Nextcloud, PostgreSQL, Redis (too critical to auto-update)
### 🌐 Multi-Server Support
Deploy identical stacks to multiple servers:
- Each server gets unique domain
- Fully independent deployments
- Shared Tailscale network (optional)
### 📊 Monitoring
- **Uptime Kuma**: Service health monitoring
- **Email Alerts**: Configurable notifications
- **Status Page**: Optional public status page
- **Container Health**: Docker healthchecks
- **SSL Monitoring**: Certificate expiry tracking
---
## 📚 Documentation
| Document | Purpose |
|----------|---------|
| [README.md](README.md) | Complete project overview, features, usage |
| [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) | Step-by-step deployment instructions |
| [TROUBLESHOOTING.md](TROUBLESHOOTING.md) | Common issues and solutions |
| [BACKUP_RESTORE.md](BACKUP_RESTORE.md) | Backup and restore procedures |
| [AGENTS.md](AGENTS.md) | Docker Compose & Ansible guidelines |
---
## 🛠️ Makefile Commands
```bash
make help # Show all commands
make setup # Run interactive setup
make ping # Test connectivity
make check # Run preflight checks
make deploy # Full deployment
make deploy-dry # Test run (no changes)
make status # Show container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
make rekey-vault # Change vault password
```
---
## 🎯 Design Principles
### 1. **User-Friendly**
- Interactive setup script (no manual configuration editing)
- Automatic SSL certificates
- Clear documentation
- Helpful error messages
### 2. **Secure by Default**
- Minimal firewall rules
- Encrypted secrets (Ansible Vault)
- Tailscale-only admin interfaces
- Automatic security updates
- No hardcoded credentials
### 3. **Production-Ready**
- Automated backups
- Monitoring included
- Health checks configured
- Documented restore procedures
- Emergency rollback playbook
### 4. **Maintainable**
- Modular playbook structure
- Clear variable naming
- Comprehensive comments
- Role-based organization
- Git-friendly (.gitignore configured)
### 5. **Safe Updates**
- Critical services excluded from auto-update
- Watchtower uses label-based control
- Manual update procedures documented
- Rollback capability included
---
## 🔧 Deployment Flow
```
┌─────────────────────────────────────────────────────────────┐
│ 1. SETUP (./setup.sh) │
│ • Collect server details, domains, credentials │
│ • Generate encrypted inventory & variables │
│ • Create Ansible Vault with secrets │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. PREFLIGHT CHECKS (01-preflight-checks.yml) │
│ • Verify Ansible version, SSH connectivity │
│ • Check disk space, ports availability │
│ • Validate DNS records, detect LXC │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. SYSTEM SETUP (02-system-setup.yml) │
│ • Update packages, install essentials │
│ • Configure UFW firewall, fail2ban │
│ • Enable automatic security updates │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. DOCKER SETUP (03-docker-setup.yml) │
│ • Remove old Docker installations │
│ • Install Docker CE + Compose v2 │
│ • Configure Docker daemon │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. TAILSCALE SETUP (04-tailscale-setup.yml) │
│ • Install Tailscale │
│ • Activate if auth key provided │
│ • Enable systemd service │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. DEPLOY STACK (05-deploy-stack.yml) │
│ • Create directory structure │
│ • Template docker-compose.yml, .env, configs │
│ • Pull Docker images, start containers │
│ • Wait for services healthy │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. CONFIGURE CADDY (06-configure-caddy.yml) │
│ • Validate Caddyfile syntax │
│ • Reload Caddy configuration │
│ • Obtain Let's Encrypt SSL certificates │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 8. SETUP BACKUPS (07-setup-backups.yml) │
│ • Create backup script │
│ • Configure cron jobs (daily/weekly) │
│ • Run test backup │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 9. POST-DEPLOYMENT (08-post-deployment.yml) │
│ • Verify all containers running │
│ • Install Nextcloud apps │
│ • Configure background jobs │
│ • Create deployment report │
└─────────────────────────────────────────────────────────────┘
✅ DEPLOYMENT COMPLETE!
```
---
## 🧩 Template Files
### docker-compose.yml.j2
Complete Docker Compose stack with:
- All 12 services configured
- Health checks for critical services
- Proper dependencies (depends_on with conditions)
- Watchtower labels for update control
- Named volumes and networks
- Environment variables from .env
**Location**: `roles/nextcloud_stack/templates/docker-compose.yml.j2`
### Caddyfile.j2
Caddy reverse proxy configuration with:
- Automatic SSL for all domains
- Public services (Nextcloud, OnlyOffice, etc.)
- Tailscale-restricted services (Homarr, Dockhand, Uptime Kuma)
- Security headers
- Large file upload support (10GB)
- Optional public status page
**Location**: `roles/caddy/templates/Caddyfile.j2`
### env.j2
Environment variables template with:
- Database credentials
- Redis password
- Nextcloud admin credentials
- Application secrets (Homarr)
- Domain configuration
- Timezone settings
**Location**: `roles/nextcloud_stack/templates/env.j2`
---
## 🔐 Security Considerations
### Secrets Management
All sensitive data encrypted with Ansible Vault:
- Database passwords
- Admin credentials
- Application secrets
- Tailscale auth keys
**Never committed in plain text!**
### Network Security
- **Public**: Only Nextcloud and productivity apps
- **Tailscale-only**: All management interfaces
- **Internal**: Database and Redis (no external access)
### Firewall Rules (UFW)
```
22/tcp - SSH
80/tcp - HTTP (redirects to HTTPS)
443/tcp - HTTPS
443/udp - HTTP/3 (QUIC)
41641/udp - Tailscale
Default: DENY
```
### Update Strategy
**Why not auto-update everything?**
- **Nextcloud**: Database migrations may fail automatically
- **PostgreSQL**: Schema changes could break Nextcloud
- **Redis**: Unlikely issues, but excluded for consistency
**Safe to auto-update:**
- Stateless applications (Excalidraw, Obsidian)
- Infrastructure (Caddy)
- Management tools (Homarr, Dockhand)
---
## 📊 Resource Usage (Approximate)
### Disk Space
- Docker images: ~5GB
- Initial volumes: ~2GB
- Backups (30 days): ~50GB
- **Total recommended**: 100GB minimum
### Memory
- PostgreSQL: ~256MB
- Redis: ~256MB (configured limit)
- Nextcloud: ~512MB
- OnlyOffice: ~2GB
- Other services: ~1GB combined
- **Total**: 4-6GB RAM recommended
### Network
- **Inbound**: 80, 443 (HTTP/HTTPS), 41641 (Tailscale)
- **Outbound**: Internet access required for Let's Encrypt, Docker images, updates
---
## 🎓 Next Steps
### Immediate
1. **Test the deployment** on a test server first
2. **Verify DNS** is configured correctly
3. **Run setup.sh** and answer questions carefully
4. **Deploy** with `make deploy`
5. **Access Nextcloud** and complete initial setup
### After Deployment
1. **Configure Uptime Kuma** monitoring
2. **Customize Homarr** dashboard
3. **Test backups** with `make backup`
4. **Enable 2FA** in Nextcloud
5. **Install additional Nextcloud apps** as needed
### Ongoing
1. **Monitor** service health in Uptime Kuma
2. **Review backups** monthly
3. **Test restore** procedures quarterly
4. **Update Nextcloud** when new versions release (manually)
5. **Check logs** for any issues
---
## 💡 Tips & Best Practices
### For LXC Containers
**On LXC host, enable nested virtualization:**
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### For Production Use
1. Use strong passwords (not example passwords)
2. Enable Nextcloud 2FA
3. Configure off-site backups with rclone
4. Set up Uptime Kuma email alerts
5. Review logs regularly
### For Multiple Servers
Each server can have:
- Different domain
- Different subdomain preferences
- Same or different Tailscale network
Just run `setup.sh` and add all servers when prompted.
---
## 🐛 Known Limitations
1. **Watchtower is archived** (December 2025)
- Still works, but no longer maintained
- Consider alternative update strategies long-term
2. **OnlyOffice needs 2GB+ RAM**
- May be slow on low-memory servers
- Consider disabling if not needed
3. **First-time deployment takes 30-45 min**
- Mostly Docker image downloads
- Subsequent deploys are faster
4. **Let's Encrypt rate limits**
- 50 certs/week per domain
- Use staging server for testing
---
## 📜 License
MIT License - Use freely, modify as needed
---
## 🙏 Acknowledgments
Built with these amazing open-source projects:
- **Nextcloud** - File sync and collaboration
- **Caddy** - Automatic HTTPS web server
- **Ansible** - IT automation
- **Docker** - Containerization
- **PostgreSQL** - Database
- **Tailscale** - Zero-config VPN
- **OnlyOffice**, **Excalidraw**, **Obsidian** - Productivity tools
- **Homarr**, **Dockhand**, **Uptime Kuma** - Management & monitoring
---
## 📞 Support
**Documentation:**
- [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) - Getting started
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Fix common issues
- [BACKUP_RESTORE.md](BACKUP_RESTORE.md) - Backup procedures
**Project Files:**
All source code, playbooks, and templates included in this repository.
---
## ✅ Checklist for New Users
Before deploying:
- [ ] Read DEPLOYMENT_GUIDE.md
- [ ] Install Ansible 2.14+ on control machine
- [ ] Have Ubuntu 20.04+ server ready
- [ ] Configure DNS A records
- [ ] Test SSH access to server
- [ ] Have domain name ready
- [ ] Decide on subdomain preferences
After deploying:
- [ ] Access Nextcloud web UI
- [ ] Complete Nextcloud setup wizard
- [ ] Setup Uptime Kuma monitoring
- [ ] Configure Homarr dashboard
- [ ] Test backup with `make backup`
- [ ] Enable Nextcloud 2FA
- [ ] Review deployment report
- [ ] Save Ansible Vault password securely
---
**Project Status**: ✅ Complete and Ready for Use
**Last Updated**: 2026-02-16
**Version**: 1.0
---
**Happy self-hosting! 🎉**

382
ansible/README.md Normal file
View File

@@ -0,0 +1,382 @@
# Nextcloud Stack - Automated Deployment with Ansible
Complete automation for deploying a self-hosted Nextcloud productivity stack on Ubuntu-based LXC/VPS servers.
## Features
- **12 Services**: Nextcloud, OnlyOffice, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma, PostgreSQL, Redis, Caddy, Watchtower
- **Automatic SSL**: Let's Encrypt certificates via Caddy
- **Secure Management**: Tailscale-only access for admin interfaces
- **Auto-updates**: Watchtower with safety exclusions for critical services
- **Automated Backups**: Daily database backups, weekly volume backups (30-day retention)
- **Monitoring**: Uptime Kuma with configurable alerts
- **Multi-server**: Deploy to multiple VPS servers with unique domains
## Quick Start
### Prerequisites
1. **Control Machine** (your laptop):
- Ansible 2.14+ installed
- SSH access to target servers
2. **Target Server(s)**:
- Ubuntu 20.04+ (LXC container or VPS)
- Root or sudo access
- Minimum 100GB disk space
- Ports 80, 443 available
3. **DNS Configuration** (BEFORE deployment):
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
### Installation
1. **Clone this repository**:
```bash
git clone <your-repo-url>
cd ansible-nextcloud-deployment
```
2. **Run interactive setup**:
```bash
./setup.sh
```
This will ask for:
- Server IP addresses and domains
- Your name and email
- Admin credentials
- Subdomain preferences
- Tailscale auth key (optional)
- Monitoring email
- Ansible Vault password
3. **Deploy the stack**:
```bash
make deploy
```
Or manually:
```bash
ansible-playbook playbooks/site.yml --ask-vault-pass
```
4. **Access your services**:
- Nextcloud: `https://cloud.yourdomain.com`
- OnlyOffice: `https://office.yourdomain.com`
- Homarr (Tailscale only): `https://home.yourdomain.com`
- Uptime Kuma (Tailscale only): `https://uptime.yourdomain.com`
## Project Structure
```
ansible-nextcloud-deployment/
├── setup.sh # Interactive configuration script
├── Makefile # Convenient command shortcuts
├── ansible.cfg # Ansible configuration
├── inventory/
│ ├── hosts.yml # Generated by setup.sh
│ └── group_vars/all/
│ ├── vars.yml # Public variables
│ └── vault.yml # Encrypted secrets
├── playbooks/
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # Pre-deployment validation
│ ├── 02-system-setup.yml # System packages & security
│ ├── 03-docker-setup.yml # Docker installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Docker Compose deployment
│ ├── 06-configure-caddy.yml # Reverse proxy configuration
│ ├── 07-setup-backups.yml # Backup automation
│ └── 08-post-deployment.yml # Final verification
└── roles/
├── nextcloud_stack/
│ └── templates/
│ ├── docker-compose.yml.j2
│ └── env.j2
└── caddy/
└── templates/
└── Caddyfile.j2
```
## Makefile Commands
```bash
make help # Show all commands
make setup # Run interactive setup
make ping # Test connectivity
make check # Run preflight checks
make deploy # Full deployment
make deploy-dry # Dry run (no changes)
make status # Show container status
make logs # View container logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
## Service Stack
| Service | Purpose | Port | Access | Auto-Update |
|---------|---------|------|--------|-------------|
| Caddy | Reverse proxy + SSL | 80, 443 | Public | ✅ |
| Nextcloud | File sync & collaboration | Internal | Public (via Caddy) | ❌ Manual |
| PostgreSQL 18 | Database | Internal | Internal only | ❌ Manual |
| Redis 7 | Cache layer | Internal | Internal only | ❌ Manual |
| OnlyOffice | Document editor | Internal | Public (via Caddy) | 👁️ Monitor |
| Excalidraw | Whiteboard tool | Internal | Public (via Caddy) | ✅ |
| Obsidian | Note-taking | Internal | Public (via Caddy) | ✅ |
| Homarr | Dashboard | Internal | Tailscale only | ✅ |
| Dockhand | Container manager | 3003 | Tailscale only | ✅ |
| Uptime Kuma | Monitoring | Internal | Tailscale only | ✅ |
| Watchtower | Auto-updater | N/A | N/A | N/A |
## Security Features
- **Firewall**: UFW configured with minimal open ports
- **Fail2ban**: SSH brute-force protection
- **Automatic Updates**: Unattended security updates enabled
- **Secret Management**: Ansible Vault encryption for all credentials
- **Access Control**: Management UIs restricted to Tailscale network
- **SSL/TLS**: Automatic Let's Encrypt certificates
- **Container Isolation**: Dedicated Docker network
## Backup System
- **Daily**: PostgreSQL database dumps (3:00 AM)
- **Weekly**: Full volume backups (Sundays)
- **Retention**: 30 days
- **Location**: `/opt/nextcloud-stack/backups/`
- **rclone**: Pre-installed for future remote backups
### Manual Backup
```bash
ssh user@server
cd /opt/nextcloud-stack
./backup.sh
```
### Restore from Backup
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for detailed procedures.
## Tailscale Setup
If you didn't provide an auth key during setup:
```bash
ssh user@server
sudo tailscale up
```
Then access management interfaces via Tailscale IP or MagicDNS.
## Updating Services
### Safe to Auto-Update (Watchtower handles)
- Caddy, Excalidraw, Obsidian, Homarr, Dockhand, Uptime Kuma
### Monitor Only (notifications, no auto-update)
- OnlyOffice
### Manual Update Required
- Nextcloud
- PostgreSQL
- Redis
### Updating Nextcloud Manually
```bash
ssh user@server
cd /opt/nextcloud-stack
# 1. Backup
./backup.sh
# 2. Enable maintenance mode
docker exec -u www-data next php occ maintenance:mode --on
# 3. Update
docker compose pull next
docker compose up -d next
# 4. Run database migrations
docker exec -u www-data next php occ upgrade
# 5. Disable maintenance mode
docker exec -u www-data next php occ maintenance:mode --off
```
## Troubleshooting
### DNS Not Configured
**Error**: Let's Encrypt fails to issue certificates
**Solution**: Ensure all DNS A records point to your server IP. Check with:
```bash
dig +short cloud.yourdomain.com
```
### LXC Container Issues
**Error**: Docker fails to start
**Solution**: On LXC host, enable nested virtualization:
```bash
lxc config set CONTAINER_NAME security.nesting true
lxc restart CONTAINER_NAME
```
### Port Already in Use
**Error**: Port 80 or 443 already bound
**Solution**: Check what's using the ports:
```bash
sudo ss -tlnp | grep ':80\|:443'
sudo systemctl stop apache2 # or nginx
```
### Nextcloud Stuck in Maintenance Mode
```bash
docker exec -u www-data next php occ maintenance:mode --off
```
### View Logs
```bash
cd /opt/nextcloud-stack
docker compose logs -f [service-name]
```
## Post-Deployment Tasks
1. **Login to Nextcloud**:
- URL: `https://cloud.yourdomain.com`
- Username: (set during setup)
- Password: (stored in vault)
2. **Setup Uptime Kuma**:
- URL: `https://uptime.yourdomain.com` (via Tailscale)
- Create admin account on first visit
- Configure monitors for your services
3. **Configure Homarr Dashboard**:
- URL: `https://home.yourdomain.com` (via Tailscale)
- Add service tiles
- Customize layout
4. **Configure OnlyOffice in Nextcloud**:
- Nextcloud Settings → Administration → Office
- Document Editing Service: `https://office.yourdomain.com`
5. **Test Backups**:
```bash
make backup
```
## File Locations on Server
```
/opt/nextcloud-stack/
├── docker-compose.yml
├── .env
├── backup.sh
├── configs/
│ ├── caddy/Caddyfile
│ └── nextcloud/
├── data/
│ ├── homarr/
│ └── obsidian/
└── backups/
├── database/
└── volumes/
```
## Environment Variables
All secrets are stored in `inventory/group_vars/all/vault.yml` (encrypted).
To edit:
```bash
make edit-vault
```
## Multiple Server Deployment
The setup script supports multiple servers. Each can have its own domain:
```bash
Server 1: 192.168.1.100 → cloud1.example.com
Server 2: 192.168.1.101 → cloud2.anotherdomain.net
Server 3: 192.168.1.102 → personal.mydomain.org
```
Deploy to all:
```bash
make deploy
```
Deploy to specific server:
```bash
ansible-playbook playbooks/site.yml --limit server_hostname --ask-vault-pass
```
## Maintenance
### Check Container Status
```bash
make status
```
### Restart Services
```bash
make restart
```
### Update Images (safe services only)
```bash
make update
```
### View Logs
```bash
make logs
```
## Uninstallation
```bash
ssh user@server
cd /opt/nextcloud-stack
docker compose down -v # WARNING: Deletes all data!
cd /opt
rm -rf nextcloud-stack
```
## Support
- See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for common issues
- See [BACKUP_RESTORE.md](BACKUP_RESTORE.md) for backup procedures
- See [AGENTS.md](AGENTS.md) for development guidelines
## License
MIT
## Acknowledgments
- Nextcloud team
- Caddy web server
- Ansible community
- All open-source contributors
---
**Built with ❤️ for self-hosting enthusiasts**

192
ansible/START_HERE.md Normal file
View File

@@ -0,0 +1,192 @@
# 👋 Welcome to Nextcloud Stack Ansible Deployment!
## 🚀 Quick Start (Choose Your Path)
### 🏃 I Want to Deploy NOW
1. Read → [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) (10 min read)
2. Run → `./setup.sh` (5-10 min)
3. Deploy → `make deploy` (30-45 min)
### 📚 I Want to Learn More First
1. Read → [README.md](README.md) - Complete project overview
2. Read → [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) - Design & architecture
3. Then → Follow Quick Start above
### 🔧 I'm Having Issues
1. Check → [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
2. Check logs → `make logs`
3. Review → [BACKUP_RESTORE.md](BACKUP_RESTORE.md) if you need to restore
---
## 📖 Documentation Index
| Document | Purpose | Read Time |
|----------|---------|-----------|
| **[DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)** | Step-by-step deployment instructions | 10 min |
| **[README.md](README.md)** | Complete project overview & features | 15 min |
| **[PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)** | Technical details & design | 10 min |
| **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** | Common issues & solutions | Reference |
| **[BACKUP_RESTORE.md](BACKUP_RESTORE.md)** | Backup & restore procedures | Reference |
| **[AGENTS.md](AGENTS.md)** | Docker & Ansible development guidelines | Reference |
---
## ✅ Pre-Deployment Checklist
Before you begin, ensure you have:
- [ ] Ubuntu 20.04+ server (LXC or VPS)
- [ ] Root or sudo access to server
- [ ] Minimum 100GB disk space on server
- [ ] Ansible 2.14+ installed on your laptop/control machine
- [ ] Domain name with DNS control
- [ ] SSH access configured to server
**Critical:** DNS A records must point to your server BEFORE deployment!
---
## 🎯 What You'll Get
After deployment, you'll have:
### Public Services (HTTPS)
- **Nextcloud** - File sync & collaboration
- **OnlyOffice** - Document editor (Word, Excel, PowerPoint)
- **Excalidraw** - Whiteboard & diagrams
- **Obsidian** - Note-taking & knowledge management
### Management Tools (Tailscale-only, secure)
- **Homarr** - Beautiful dashboard for all services
- **Dockhand** - Container management interface
- **Uptime Kuma** - Monitoring & alerts
### Infrastructure (automatic, invisible)
- **Caddy** - Reverse proxy with automatic SSL
- **PostgreSQL** - Database
- **Redis** - Cache for better performance
- **Watchtower** - Automatic updates (safe services only)
---
## 💡 Key Features
-**One-command deployment** - `make deploy` does everything
-**Automatic SSL** - Let's Encrypt certificates via Caddy
-**Secure by default** - Firewall, fail2ban, Vault encryption
-**Auto backups** - Daily DB, weekly volumes (30-day retention)
-**Multi-server** - Deploy to multiple VPS with unique domains
-**Production-ready** - Monitoring, backups, documentation
---
## 🏗️ Project Structure
```
/home/liph/programming/ansible/
├── setup.sh ⭐ # Start here! Interactive setup
├── Makefile # Convenient commands (make deploy)
├── playbooks/ # 10 Ansible playbooks
│ ├── site.yml # Main orchestrator
│ ├── 01-preflight-checks.yml # Validates environment
│ ├── 02-system-setup.yml # Packages & security
│ ├── 03-docker-setup.yml # Docker installation
│ ├── 04-tailscale-setup.yml # VPN setup
│ ├── 05-deploy-stack.yml # Main deployment
│ ├── 06-configure-caddy.yml # SSL & reverse proxy
│ ├── 07-setup-backups.yml # Backup automation
│ ├── 08-post-deployment.yml # Verification
│ └── 99-rollback.yml # Emergency rollback
├── roles/ # Templates & configs
│ ├── nextcloud_stack/
│ │ └── templates/
│ │ ├── docker-compose.yml.j2 # 12-service stack
│ │ └── env.j2 # Environment vars
│ └── caddy/
│ └── templates/
│ └── Caddyfile.j2 # Reverse proxy config
└── docs/ # You are here!
```
---
## 🎓 Common Commands
After setup, you'll use these frequently:
```bash
make deploy # Deploy everything
make status # Check container status
make logs # View all logs
make backup # Run manual backup
make restart # Restart all containers
make edit-vault # Edit encrypted secrets
```
---
## 📞 Need Help?
1. **Common Issues**: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
2. **Backup/Restore**: [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
3. **Full Documentation**: [README.md](README.md)
---
## ⚡ 30-Second Overview
This project automates deploying a complete self-hosted productivity stack:
1. **Configure once**: `./setup.sh` collects all info
2. **Deploy anywhere**: `make deploy` to any Ubuntu server
3. **Use immediately**: Access via `https://cloud.yourdomain.com`
**Time to deploy:** 30-45 minutes (mostly automatic)
---
## 🎯 Recommended First Steps
### For Complete Beginners
1. Read [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
2. Configure DNS for your domain
3. Run `./setup.sh` on your laptop
4. Run `make deploy` and wait
5. Access your new Nextcloud!
### For Experienced Users
1. Skim [README.md](README.md) to understand the stack
2. Review [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md) for architecture
3. Run `./setup.sh` and `make deploy`
4. Review generated configs in `/opt/nextcloud-stack/`
### For Developers
1. Read [AGENTS.md](AGENTS.md) for development guidelines
2. Review playbooks in `playbooks/` directory
3. Check templates in `roles/*/templates/`
4. Customize as needed for your use case
---
## 🌟 What Makes This Special?
- **User-friendly**: Interactive setup, no manual config editing
- **Secure**: Ansible Vault, firewalls, Tailscale, automatic updates
- **Complete**: Monitoring, backups, SSL, documentation included
- **Production-ready**: Tested deployment procedures, rollback capability
- **Maintainable**: Clean code, comprehensive docs, modular design
---
## 🚦 Ready to Begin?
**New users**: Start with [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md)
**Quick reference**: See [README.md](README.md)
**Technical details**: Check [PROJECT_SUMMARY.md](PROJECT_SUMMARY.md)
---
**Let's build your self-hosted cloud! 🚀**

509
ansible/TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,509 @@
# Troubleshooting Guide
Common issues and solutions for Nextcloud Stack deployment.
## Table of Contents
- [DNS Issues](#dns-issues)
- [SSL Certificate Problems](#ssl-certificate-problems)
- [Docker Issues](#docker-issues)
- [LXC Container Issues](#lxc-container-issues)
- [Nextcloud Issues](#nextcloud-issues)
- [Database Connection Issues](#database-connection-issues)
- [Tailscale Issues](#tailscale-issues)
- [Port Conflicts](#port-conflicts)
- [Permission Issues](#permission-issues)
---
## DNS Issues
### Problem: DNS records not resolving
**Symptoms:**
- Let's Encrypt fails to issue certificates
- Caddy shows certificate errors
- Services inaccessible via domain
**Diagnosis:**
```bash
dig +short cloud.yourdomain.com @8.8.8.8
```
**Solution:**
1. Ensure all required A records point to your server IP
2. Wait for DNS propagation (up to 48 hours, usually minutes)
3. Use [DNSChecker.org](https://dnschecker.org) to verify global propagation
**Required DNS Records:**
```
cloud.yourdomain.com → YOUR_SERVER_IP
office.yourdomain.com → YOUR_SERVER_IP
draw.yourdomain.com → YOUR_SERVER_IP
notes.yourdomain.com → YOUR_SERVER_IP
home.yourdomain.com → YOUR_SERVER_IP
manage.yourdomain.com → YOUR_SERVER_IP
uptime.yourdomain.com → YOUR_SERVER_IP
```
**Temporary Workaround:**
Edit `/etc/hosts` on your local machine:
```
YOUR_SERVER_IP cloud.yourdomain.com
```
---
## SSL Certificate Problems
### Problem: Let's Encrypt rate limit exceeded
**Symptoms:**
- Error: "too many certificates already issued"
**Solution:**
1. Use Let's Encrypt staging server for testing
2. Edit Caddyfile (add to global options):
```caddy
{
email {{ user_email }}
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
```
3. Reload Caddy: `docker exec caddy caddy reload`
4. After testing, remove staging server line
**Rate Limits:**
- 50 certificates per domain per week
- 5 duplicate certificates per week
### Problem: Certificate validation failed
**Symptoms:**
- "Failed to verify" errors in Caddy logs
**Diagnosis:**
```bash
docker logs caddy
```
**Common Causes:**
1. DNS not pointing to server
2. Firewall blocking port 80/443
3. Another service using port 80/443
**Solution:**
```bash
# Check firewall
sudo ufw status
# Check port usage
sudo ss -tlnp | grep ':80\|:443'
# Check DNS
dig +short yourdomain.com
```
---
## Docker Issues
### Problem: Docker daemon won't start
**Symptoms:**
- `docker ps` fails
- Error: "Cannot connect to Docker daemon"
**Diagnosis:**
```bash
sudo systemctl status docker
sudo journalctl -xu docker
```
**Solution:**
```bash
sudo systemctl restart docker
```
### Problem: Containers keep restarting
**Diagnosis:**
```bash
cd /opt/nextcloud-stack
docker compose logs [service-name]
```
**Common Causes:**
1. Configuration errors
2. Port conflicts
3. Missing dependencies
**Solution:**
```bash
# Check specific container
docker logs next-db
docker logs next
docker logs caddy
# Restart specific service
docker compose restart next
```
---
## LXC Container Issues
### Problem: Docker fails to start in LXC
**Symptoms:**
- Error: "cgroups: cgroup mountpoint does not exist"
- Docker daemon fails to start
**Diagnosis:**
```bash
systemd-detect-virt # Should show "lxc"
```
**Solution on LXC Host:**
```bash
# Set security nesting
lxc config set CONTAINER_NAME security.nesting true
# May also need privileged mode
lxc config set CONTAINER_NAME security.privileged true
# Restart container
lxc restart CONTAINER_NAME
```
**Inside LXC Container:**
```bash
# Verify cgroups
mount | grep cgroup
# Check Docker status
sudo systemctl status docker
```
### Problem: AppArmor denials in LXC
**Solution on LXC Host:**
```bash
lxc config set CONTAINER_NAME raw.lxc "lxc.apparmor.profile=unconfined"
lxc restart CONTAINER_NAME
```
---
## Nextcloud Issues
### Problem: Nextcloud stuck in maintenance mode
**Symptoms:**
- Web interface shows "System in maintenance mode"
**Solution:**
```bash
docker exec -u www-data next php occ maintenance:mode --off
```
### Problem: Trusted domain error
**Symptoms:**
- "Access through untrusted domain" error
**Solution:**
```bash
docker exec -u www-data next php occ config:system:set trusted_domains 1 --value=cloud.yourdomain.com
```
### Problem: Redis connection failed
**Diagnosis:**
```bash
docker logs next-redis
docker exec next-redis redis-cli ping
```
**Solution:**
```bash
# Reconfigure Redis in Nextcloud
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
docker exec -u www-data next php occ config:system:set redis port --value=6379
```
### Problem: File uploads fail
**Symptoms:**
- Large files won't upload
- Error 413 (Payload Too Large)
**Solution:**
Already configured in Caddyfile for 10GB uploads. Check:
```bash
docker exec -u www-data next php occ config:system:get max_upload
```
### Problem: OnlyOffice integration not working
**Solution:**
```bash
# Install OnlyOffice app
docker exec -u www-data next php occ app:install onlyoffice
# Configure document server URL
docker exec -u www-data next php occ config:app:set onlyoffice DocumentServerUrl --value="https://office.yourdomain.com/"
# Disable JWT (or configure if needed)
docker exec -u www-data next php occ config:app:set onlyoffice jwt_secret --value=""
```
---
## Database Connection Issues
### Problem: Nextcloud can't connect to database
**Symptoms:**
- Error: "SQLSTATE[08006]"
- Nextcloud shows database error
**Diagnosis:**
```bash
# Check if PostgreSQL is running
docker ps | grep next-db
# Check PostgreSQL logs
docker logs next-db
# Test connection
docker exec next-db pg_isready -U nextcloud
```
**Solution:**
```bash
# Restart database
docker compose restart next-db
# Wait for it to be healthy
docker exec next-db pg_isready -U nextcloud
# Restart Nextcloud
docker compose restart next
```
### Problem: Database initialization failed
**Symptoms:**
- PostgreSQL container keeps restarting
- Empty database
**Solution:**
```bash
# Remove volumes and recreate
cd /opt/nextcloud-stack
docker compose down -v
docker compose up -d
```
**⚠️ WARNING:** This deletes all data! Only use for fresh installations.
---
## Tailscale Issues
### Problem: Can't access Tailscale-only services
**Symptoms:**
- Homarr, Dockhand, Uptime Kuma return 403 Forbidden
**Diagnosis:**
```bash
# Check if Tailscale is running
sudo tailscale status
# Get Tailscale IP
tailscale ip -4
```
**Solution:**
```bash
# Activate Tailscale (if not done)
sudo tailscale up
# Verify connection
tailscale status
```
**Access via:**
- Tailscale IP: `https://100.64.x.x:PORT`
- MagicDNS: `https://hostname.tailnet-name.ts.net`
### Problem: Tailscale not installed
**Solution:**
```bash
# Re-run Tailscale playbook
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass
```
---
## Port Conflicts
### Problem: Port 80 or 443 already in use
**Symptoms:**
- Error: "bind: address already in use"
- Caddy won't start
**Diagnosis:**
```bash
sudo ss -tlnp | grep ':80\|:443'
```
**Common Culprits:**
- Apache2
- Nginx
- Another Caddy instance
**Solution:**
```bash
# Stop conflicting service
sudo systemctl stop apache2
sudo systemctl disable apache2
# OR
sudo systemctl stop nginx
sudo systemctl disable nginx
# Restart Caddy
docker compose restart caddy
```
---
## Permission Issues
### Problem: Permission denied errors in Nextcloud
**Symptoms:**
- Can't upload files
- Can't install apps
**Diagnosis:**
```bash
# Check file permissions
docker exec next ls -la /var/www/html
```
**Solution:**
```bash
# Fix permissions (run inside container)
docker exec next chown -R www-data:www-data /var/www/html
```
### Problem: Docker socket permission denied
**Symptoms:**
- Homarr or Dockhand can't see containers
**Solution:**
Docker socket is mounted read-only by design for security.
This is normal and expected.
---
## Emergency Commands
### Completely restart the stack
```bash
cd /opt/nextcloud-stack
docker compose down
docker compose up -d
```
### View all logs in real-time
```bash
cd /opt/nextcloud-stack
docker compose logs -f
```
### Check container health
```bash
docker compose ps
docker inspect --format='{{.State.Health.Status}}' next
```
### Rebuild a specific container
```bash
docker compose up -d --force-recreate --no-deps next
```
### Emergency backup
```bash
/opt/nextcloud-stack/backup.sh
```
### Reset Nextcloud admin password
```bash
docker exec -u www-data next php occ user:resetpassword admin
```
---
## Getting Help
If none of these solutions work:
1. **Check logs:**
```bash
docker compose logs [service-name]
```
2. **Check system logs:**
```bash
sudo journalctl -xe
```
3. **Verify configuration:**
```bash
cat /opt/nextcloud-stack/docker-compose.yml
cat /opt/nextcloud-stack/.env
```
4. **Test connectivity:**
```bash
curl -I https://cloud.yourdomain.com
docker exec caddy caddy validate
```
5. **Deployment report:**
```bash
cat /opt/nextcloud-stack/DEPLOYMENT.txt
```
---
## Recovery Procedures
### Restore from backup
See [BACKUP_RESTORE.md](BACKUP_RESTORE.md)
### Complete reinstallation
```bash
# 1. Backup first!
/opt/nextcloud-stack/backup.sh
# 2. Remove deployment
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
# 3. Redeploy
ansible-playbook playbooks/site.yml --ask-vault-pass
```
---
**Last Updated:** 2026-02-16

35
ansible/ansible.cfg Normal file
View File

@@ -0,0 +1,35 @@
# Ansible Configuration File
# Generated for Nextcloud Stack Deployment
[defaults]
inventory = inventory/hosts.yml
roles_path = roles
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
stdout_callback = yaml
callbacks_enabled = profile_tasks, timer
deprecation_warnings = False
# SSH settings
timeout = 30
forks = 5
# Privilege escalation
become = True
become_method = sudo
become_ask_pass = False
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

7
ansible/nextcloud/.env Executable file
View File

@@ -0,0 +1,7 @@
DB_PASSWORD=
DB_USERNAME=
DB_DATABASE_NAME=
DB_HOST=
PUID=33
PGID=1000

View File

@@ -0,0 +1,108 @@
services:
excalidraw:
container_name: excalidraw
image: docker.io/excalidraw/excalidraw:latest
restart: unless-stopped
ports:
- "3009:80"
environment:
- NODE_ENV=production
- TZ=Europe/Zurich
# Database (PostgreSQL)
next-db:
image: docker.io/postgres:18
container_name: next-db
environment:
- POSTGRES_DB=${DB_DATABASE_NAME}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
-
restart: unless-stopped
networks:
- nextcloud_network
# # Redis Cache
# next_redis:
# image: docker.io/redis:latest
# container_name: next-redis
# command: redis-server --save 60 1 --loglevel warning
# volumes:
# - ./data/redis:/data
# restart: unless-stopped
# networks:
# - nextcloud_network
# Nextcloud Main Application
next:
image: docker.io/nextcloud:latest
container_name: next
depends_on:
- next-db
mem_swappiness: -1
ports:
- "8808:80"
environment:
- POSTGRES_DB=${DB_DATABASE_NAME}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_HOST=${DB_HOST}
- NEXTCLOUD_TRUSTED_DOMAINS=next.liphlink.xyz
- NEXTCLOUD_ADMIN_USER=liph
- NEXTCLOUD_ADMIN_PASSWORD=1ChagenexT
volumes:
- :/var/www/html
- :/var/www/html/config:Z
- :/var/www/html/custom_apps
restart: unless-stopped
networks:
- nextcloud_network
# OnlyOffice (alternative to Collabora)
onlyoffice:
image: docker.io/onlyoffice/documentserver:latest
container_name: onlyoffice
ports:
- 8000:80
environment:
# - JWT_SECRET='S2jaRmOxSxnFoOMhzXotL1QTptDuqhom'
- JWT_ENABLED=false
- JWT_HEADER=Authorization
- JWT_IN_BODY=true
volumes:
- :/var/www/onlyoffice/Data
restart: unless-stopped
networks:
- nextcloud_network
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
security_opt:
- seccomp:unconfined #optional
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- :/config:z
- :/vault:z
ports:
- 3005:3000
- 3004:3001
shm_size: "1gb"
restart: unless-stopped
volumes:
pg_data:
redis_data:
nextcloud_data:
nextcloud_config:
nextcloud_apps:
nextcloud_db_data:
onlyoffice_data:
networks:
nextcloud_network:
name: nextcloud_network

View File

@@ -0,0 +1,98 @@
---
# Playbook 01: Preflight Checks
# Validates environment before deployment
- name: Preflight Checks
hosts: all
gather_facts: yes
become: no
tasks:
- name: Check Ansible version
assert:
that:
- ansible_version.full is version('2.14', '>=')
fail_msg: "Ansible 2.14 or higher is required"
success_msg: "Ansible version OK ({{ ansible_version.full }})"
delegate_to: localhost
run_once: true
- name: Test SSH connectivity
ping:
- name: Check sudo access
command: sudo -n true
changed_when: false
- name: Check Python3 availability
command: python3 --version
register: python_version
changed_when: false
- name: Display Python version
debug:
msg: "Python version: {{ python_version.stdout }}"
- name: Check disk space
shell: df -h / | awk 'NR==2 {print $4}'
register: disk_space
changed_when: false
- name: Validate sufficient disk space
assert:
that:
- disk_space.stdout is regex('[0-9]+G')
fail_msg: "Insufficient disk space. At least 20GB recommended."
success_msg: "Disk space OK ({{ disk_space.stdout }} available)"
- name: Check if ports 80 and 443 are available
wait_for:
port: "{{ item }}"
state: stopped
timeout: 1
loop:
- 80
- 443
ignore_errors: yes
register: port_check
- name: Detect virtualization type
command: systemd-detect-virt
register: virt_type
changed_when: false
failed_when: false
- name: Warn if running in LXC
debug:
msg: |
⚠️ RUNNING IN LXC CONTAINER
Docker requires nested virtualization.
Ensure on LXC host: lxc config set {{ inventory_hostname }} security.nesting true
when: "'lxc' in virt_type.stdout"
- name: Validate DNS resolution for all subdomains
command: dig +short {{ item }}.{{ domain }} @8.8.8.8
register: dns_check
changed_when: false
failed_when: false
loop:
- "{{ subdomain_nextcloud }}"
- "{{ subdomain_office }}"
- "{{ subdomain_draw }}"
- "{{ subdomain_notes }}"
- "{{ subdomain_homarr }}"
- "{{ subdomain_dockhand }}"
- "{{ subdomain_uptime }}"
- name: Display DNS check results
debug:
msg: "{{ item.item }}.{{ domain }} → {{ item.stdout if item.stdout else 'NOT CONFIGURED' }}"
loop: "{{ dns_check.results }}"
loop_control:
label: "{{ item.item }}.{{ domain }}"
- name: Preflight checks complete
debug:
msg: |
✓ All preflight checks passed
✓ Ready to proceed with deployment

View File

@@ -0,0 +1,119 @@
---
# Playbook 02: System Setup
# Install packages, configure firewall and security
- name: System Setup
hosts: all
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Upgrade all packages
apt:
upgrade: dist
autoremove: yes
autoclean: yes
- name: Install essential packages
apt:
name:
- curl
- wget
- git
- vim
- htop
- net-tools
- dnsutils
- ufw
- fail2ban
- unattended-upgrades
- apt-transport-https
- ca-certificates
- gnupg
- lsb-release
- software-properties-common
- python3-pip
- python3-docker
- rsync
- "{% if install_rclone %}rclone{% endif %}"
state: present
- name: Configure UFW - Allow SSH
ufw:
rule: allow
port: '22'
proto: tcp
- name: Configure UFW - Allow HTTP
ufw:
rule: allow
port: '80'
proto: tcp
- name: Configure UFW - Allow HTTPS TCP
ufw:
rule: allow
port: '443'
proto: tcp
- name: Configure UFW - Allow HTTPS UDP (HTTP/3)
ufw:
rule: allow
port: '443'
proto: udp
- name: Configure UFW - Allow Tailscale
ufw:
rule: allow
port: '41641'
proto: udp
- name: Enable UFW
ufw:
state: enabled
policy: deny
- name: Configure fail2ban for SSH
copy:
dest: /etc/fail2ban/jail.local
content: |
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 600
notify: restart fail2ban
- name: Enable unattended security updates
copy:
dest: /etc/apt/apt.conf.d/20auto-upgrades
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
- name: Set timezone
timezone:
name: "{{ timezone }}"
- name: Set kernel parameters for Docker
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
reload: yes
loop:
- { key: 'net.ipv4.ip_forward', value: '1' }
- { key: 'fs.inotify.max_user_watches', value: '524288' }
handlers:
- name: restart fail2ban
service:
name: fail2ban
state: restarted

View File

@@ -0,0 +1,115 @@
---
# Playbook 03: Docker Setup
# Install Docker CE and Docker Compose v2
- name: Docker Installation
hosts: all
become: yes
tasks:
- name: Remove old Docker installations
apt:
name:
- docker
- docker-engine
- docker.io
- containerd
- runc
- docker-compose
state: absent
purge: yes
- name: Remove old Docker data directories
file:
path: "{{ item }}"
state: absent
loop:
- /var/lib/docker
- /var/lib/containerd
- /etc/docker
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker CE
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: present
update_cache: yes
- name: Create Docker daemon configuration
copy:
dest: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"userland-proxy": false,
"live-restore": true
}
notify: restart docker
- name: Create docker group
group:
name: docker
state: present
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Create deployment directory
file:
path: "{{ deployment_dir }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Start and enable Docker
service:
name: docker
state: started
enabled: yes
- name: Verify Docker installation
command: docker --version
register: docker_version
changed_when: false
- name: Display Docker version
debug:
msg: "Docker installed: {{ docker_version.stdout }}"
- name: Verify Docker Compose installation
command: docker compose version
register: compose_version
changed_when: false
- name: Display Docker Compose version
debug:
msg: "Docker Compose installed: {{ compose_version.stdout }}"
handlers:
- name: restart docker
service:
name: docker
state: restarted

View File

@@ -0,0 +1,60 @@
---
# Playbook 04: Tailscale Setup
# Install and optionally activate Tailscale VPN
- name: Tailscale Installation
hosts: all
become: yes
tasks:
- name: Add Tailscale GPG key
apt_key:
url: https://pkgs.tailscale.com/stable/ubuntu/{{ ansible_distribution_release }}.noarmor.gpg
state: present
- name: Add Tailscale repository
apt_repository:
repo: "deb https://pkgs.tailscale.com/stable/ubuntu {{ ansible_distribution_release }} main"
state: present
- name: Install Tailscale
apt:
name: tailscale
state: present
update_cache: yes
- name: Check if Tailscale auth key is provided
set_fact:
tailscale_auto_enable: "{{ tailscale_auth_key is defined and tailscale_auth_key != '' }}"
- name: Activate Tailscale (if auth key provided)
command: tailscale up --authkey={{ tailscale_auth_key }} --advertise-tags=tag:nextcloud
when: tailscale_auto_enable
register: tailscale_activation
- name: Get Tailscale IP (if activated)
command: tailscale ip -4
register: tailscale_ip
when: tailscale_auto_enable
changed_when: false
- name: Display Tailscale status (activated)
debug:
msg: |
✓ Tailscale activated
IP: {{ tailscale_ip.stdout }}
when: tailscale_auto_enable
- name: Display manual activation instructions (not activated)
debug:
msg: |
Tailscale installed but not activated.
To enable, run on the server:
sudo tailscale up
when: not tailscale_auto_enable
- name: Enable Tailscale service
service:
name: tailscaled
state: started
enabled: yes

View File

@@ -0,0 +1,109 @@
---
# Playbook 05: Deploy Stack
# Deploy Docker Compose stack with all services
- name: Deploy Nextcloud Stack
hosts: all
become: yes
tasks:
- name: Create directory structure
file:
path: "{{ item }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
loop:
- "{{ deployment_dir }}/configs"
- "{{ deployment_dir }}/configs/caddy"
- "{{ deployment_dir }}/configs/nextcloud"
- "{{ deployment_dir }}/data"
- "{{ deployment_dir }}/data/homarr"
- "{{ deployment_dir }}/data/obsidian/config"
- "{{ deployment_dir }}/data/obsidian/vault"
- "{{ deployment_dir }}/backups"
- "{{ deployment_dir }}/backups/database"
- "{{ deployment_dir }}/backups/volumes"
- name: Template docker-compose.yml
template:
src: ../roles/nextcloud_stack/templates/docker-compose.yml.j2
dest: "{{ deployment_dir }}/docker-compose.yml"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
- name: Template .env file
template:
src: ../roles/nextcloud_stack/templates/env.j2
dest: "{{ deployment_dir }}/.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
no_log: true
- name: Template Caddyfile
template:
src: ../roles/caddy/templates/Caddyfile.j2
dest: "{{ deployment_dir }}/configs/caddy/Caddyfile"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
- name: Pull Docker images
command: docker compose pull
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
- name: Start Docker Compose stack
command: docker compose up -d
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
- name: Wait for PostgreSQL to be healthy
command: docker exec next-db pg_isready -U {{ db_user }}
register: pg_ready
until: pg_ready.rc == 0
retries: 30
delay: 10
changed_when: false
- name: Wait for Nextcloud to be accessible
uri:
url: http://localhost:8808/status.php
status_code: 200
register: nc_status
until: nc_status.status == 200
retries: 30
delay: 10
- name: Configure Nextcloud Redis cache
command: |
docker exec -u www-data next php occ config:system:set redis host --value=next-redis
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Configure Nextcloud Redis port
command: |
docker exec -u www-data next php occ config:system:set redis port --value=6379
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Configure Nextcloud memcache
command: |
docker exec -u www-data next php occ config:system:set memcache.locking --value='\\OC\\Memcache\\Redis'
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Display deployment success
debug:
msg: |
✓ Docker Compose stack deployed
✓ All containers started
✓ Nextcloud is accessible

View File

@@ -0,0 +1,48 @@
---
# Playbook 06: Configure Caddy
# Setup reverse proxy and obtain SSL certificates
- name: Configure Caddy Reverse Proxy
hosts: all
become: yes
tasks:
- name: Validate Caddyfile syntax
command: docker exec caddy caddy validate --config /etc/caddy/Caddyfile
args:
chdir: "{{ deployment_dir }}"
register: caddy_validate
failed_when: caddy_validate.rc != 0
changed_when: false
- name: Reload Caddy configuration
command: docker exec caddy caddy reload --config /etc/caddy/Caddyfile
args:
chdir: "{{ deployment_dir }}"
- name: Wait for SSL certificates (may take 1-2 minutes)
pause:
seconds: 30
prompt: "Waiting for Let's Encrypt to issue certificates..."
- name: Test HTTPS endpoint for Nextcloud
uri:
url: "https://{{ subdomain_nextcloud }}.{{ domain }}/status.php"
validate_certs: yes
status_code: 200
register: https_test
until: https_test.status == 200
retries: 10
delay: 10
ignore_errors: yes
- name: Display Caddy status
debug:
msg: |
✓ Caddyfile validated
✓ Caddy reloaded
{% if https_test.status == 200 %}
✓ HTTPS working: https://{{ subdomain_nextcloud }}.{{ domain }}
{% else %}
⚠ HTTPS check failed - verify DNS and firewall
{% endif %}

View File

@@ -0,0 +1,87 @@
---
# Playbook 07: Setup Backups
# Configure automated backup system
- name: Setup Backup System
hosts: all
become: yes
tasks:
- name: Create backup script
copy:
dest: "{{ deployment_dir }}/backup.sh"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
content: |
#!/bin/bash
# Nextcloud Stack Backup Script
set -euo pipefail
BACKUP_DIR="{{ deployment_dir }}/backups"
DB_BACKUP_DIR="$BACKUP_DIR/database"
VOLUME_BACKUP_DIR="$BACKUP_DIR/volumes"
RETENTION_DAYS={{ backup_retention_days }}
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="$BACKUP_DIR/backup.log"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "Starting backup process..."
# Database backup
log "Backing up PostgreSQL database..."
docker exec next-db pg_dump -U {{ db_user }} {{ db_name }} | \
gzip > "$DB_BACKUP_DIR/nextcloud_db_$DATE.sql.gz"
if [ $? -eq 0 ]; then
log "Database backup completed: nextcloud_db_$DATE.sql.gz"
else
log "ERROR: Database backup failed!"
exit 1
fi
# Weekly volume backup (Sundays only)
if [ $(date +%u) -eq 7 ]; then
log "Weekly volume backup (Sunday)..."
docker exec -u www-data next php occ maintenance:mode --on
tar -czf "$VOLUME_BACKUP_DIR/nextcloud_data_$DATE.tar.gz" \
-C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data . 2>/dev/null || true
tar -czf "$VOLUME_BACKUP_DIR/configs_$DATE.tar.gz" \
-C {{ deployment_dir }}/configs . 2>/dev/null || true
docker exec -u www-data next php occ maintenance:mode --off
log "Volume backup completed"
fi
# Cleanup old backups
log "Cleaning up backups older than $RETENTION_DAYS days..."
find "$DB_BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
find "$VOLUME_BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
log "Backup process completed successfully!"
- name: Create cron job for daily backups
cron:
name: "Nextcloud Stack Backup"
minute: "0"
hour: "3"
job: "{{ deployment_dir }}/backup.sh >> {{ deployment_dir }}/backups/backup.log 2>&1"
user: root
- name: Run initial test backup
command: "{{ deployment_dir }}/backup.sh"
register: backup_test
ignore_errors: yes
- name: Display backup status
debug:
msg: |
✓ Backup script created
✓ Cron job configured (daily 3:00 AM)
✓ Test backup completed
Location: {{ deployment_dir }}/backups/

View File

@@ -0,0 +1,154 @@
---
# Playbook 08: Post-Deployment
# Final verification and configuration
- name: Post-Deployment Tasks
hosts: all
become: yes
tasks:
- name: Verify all containers are running
command: docker compose ps --format json
args:
chdir: "{{ deployment_dir }}"
register: container_status
changed_when: false
- name: Check Nextcloud status
command: docker exec -u www-data next php occ status
args:
chdir: "{{ deployment_dir }}"
register: nc_status
changed_when: false
ignore_errors: yes
- name: Install recommended Nextcloud apps
command: docker exec -u www-data next php occ app:install {{ item }}
args:
chdir: "{{ deployment_dir }}"
loop:
- calendar
- contacts
- tasks
- notes
ignore_errors: yes
- name: Configure Nextcloud background jobs
command: docker exec -u www-data next php occ background:cron
args:
chdir: "{{ deployment_dir }}"
ignore_errors: yes
- name: Setup Nextcloud cron job
cron:
name: "Nextcloud Background Jobs"
minute: "*/5"
job: "docker exec -u www-data next php /var/www/html/cron.php"
user: root
- name: Get Tailscale IP (if activated)
command: tailscale ip -4
register: tailscale_ip_result
changed_when: false
failed_when: false
- name: Create deployment report
copy:
dest: "{{ deployment_dir }}/DEPLOYMENT.txt"
content: |
════════════════════════════════════════════════════════════
NEXTCLOUD STACK DEPLOYMENT REPORT
════════════════════════════════════════════════════════════
Server: {{ inventory_hostname }}
IP Address: {{ ansible_host }}
Domain: {{ domain }}
Deployment Date: {{ ansible_date_time.iso8601 }}
────────────────────────────────────────────────────────────
PUBLIC SERVICES (HTTPS)
────────────────────────────────────────────────────────────
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
────────────────────────────────────────────────────────────
MANAGEMENT SERVICES (Tailscale only)
────────────────────────────────────────────────────────────
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
────────────────────────────────────────────────────────────
CREDENTIALS
────────────────────────────────────────────────────────────
Nextcloud Admin User: {{ admin_user }}
Nextcloud Admin Password: [stored in Ansible vault]
────────────────────────────────────────────────────────────
TAILSCALE
────────────────────────────────────────────────────────────
{% if tailscale_ip_result.rc == 0 %}
Status: Connected
Tailscale IP: {{ tailscale_ip_result.stdout }}
{% else %}
Status: Not activated
Activate with: sudo tailscale up
{% endif %}
────────────────────────────────────────────────────────────
BACKUPS
────────────────────────────────────────────────────────────
Schedule: Daily at 3:00 AM
Location: {{ deployment_dir }}/backups/
Retention: {{ backup_retention_days }} days
────────────────────────────────────────────────────────────
USEFUL COMMANDS
────────────────────────────────────────────────────────────
View containers: cd {{ deployment_dir }} && docker compose ps
View logs: cd {{ deployment_dir }} && docker compose logs -f
Restart service: cd {{ deployment_dir }} && docker compose restart [service]
Manual backup: {{ deployment_dir }}/backup.sh
Nextcloud CLI: docker exec -u www-data next php occ [command]
════════════════════════════════════════════════════════════
- name: Display deployment summary
debug:
msg: |
╔════════════════════════════════════════════════════════════╗
║ DEPLOYMENT COMPLETED SUCCESSFULLY! ║
╚════════════════════════════════════════════════════════════╝
Server: {{ inventory_hostname }} ({{ ansible_host }})
Domain: {{ domain }}
📦 Public Services:
• Nextcloud: https://{{ subdomain_nextcloud }}.{{ domain }}
• OnlyOffice: https://{{ subdomain_office }}.{{ domain }}
• Excalidraw: https://{{ subdomain_draw }}.{{ domain }}
• Obsidian: https://{{ subdomain_notes }}.{{ domain }}
🔒 Management (Tailscale only):
• Homarr: https://{{ subdomain_homarr }}.{{ domain }}
• Dockhand: https://{{ subdomain_dockhand }}.{{ domain }}
• Uptime Kuma: https://{{ subdomain_uptime }}.{{ domain }}
👤 Nextcloud Admin:
Username: {{ admin_user }}
Password: [check vault]
💾 Backups:
Daily at 3:00 AM
Location: {{ deployment_dir }}/backups/
📝 Next Steps:
1. Login to Nextcloud and complete setup
2. Setup Uptime Kuma monitoring (via Tailscale)
3. Configure Homarr dashboard (via Tailscale)
4. Review deployment report: {{ deployment_dir }}/DEPLOYMENT.txt
Deployment report saved to:
{{ deployment_dir }}/DEPLOYMENT.txt

View File

@@ -0,0 +1,41 @@
---
# Playbook 99: Emergency Rollback
# Stop and remove the deployment (keeps backups)
- name: Emergency Rollback
hosts: all
become: yes
tasks:
- name: Confirm rollback
pause:
prompt: |
⚠️ WARNING: This will stop and remove all containers!
Backups in {{ deployment_dir }}/backups/ will be preserved.
Press ENTER to continue or Ctrl+C to cancel
- name: Stop Docker Compose stack
command: docker compose down
args:
chdir: "{{ deployment_dir }}"
become_user: "{{ ansible_user }}"
ignore_errors: yes
- name: Remove deployment directory (except backups)
shell: |
cd {{ deployment_dir }}
rm -rf configs data docker-compose.yml .env
ls -la
args:
warn: false
- name: Display rollback status
debug:
msg: |
✓ Containers stopped and removed
✓ Deployment files removed
✓ Backups preserved in {{ deployment_dir }}/backups/
✓ Docker volumes preserved (use 'docker volume ls' to see)
To completely remove volumes (DELETES ALL DATA):
cd {{ deployment_dir }} && docker compose down -v

View File

@@ -0,0 +1,26 @@
---
# Main Playbook - Nextcloud Stack Deployment
# This orchestrates the complete deployment process
- name: Nextcloud Stack - Complete Deployment
hosts: all
gather_facts: yes
pre_tasks:
- name: Display deployment banner
debug:
msg: |
╔════════════════════════════════════════════════════════════╗
║ Nextcloud Stack Deployment Starting ║
║ Target: {{ inventory_hostname }} ({{ ansible_host }}) ║
║ Domain: {{ domain }} ║
╚════════════════════════════════════════════════════════════╝
- import_playbook: 01-preflight-checks.yml
- import_playbook: 02-system-setup.yml
- import_playbook: 03-docker-setup.yml
- import_playbook: 04-tailscale-setup.yml
- import_playbook: 05-deploy-stack.yml
- import_playbook: 06-configure-caddy.yml
- import_playbook: 07-setup-backups.yml
- import_playbook: 08-post-deployment.yml

View File

@@ -0,0 +1,128 @@
# Caddyfile - Generated by Ansible
# Domain: {{ domain }}
# Global options
{
email {{ user_email }}
}
# ===== PUBLIC SERVICES =====
# Nextcloud
{{ subdomain_nextcloud }}.{{ domain }} {
reverse_proxy next:80
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy no-referrer
X-XSS-Protection "1; mode=block"
-Server
}
redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301
redir /.well-known/webfinger /index.php/.well-known/webfinger 301
redir /.well-known/nodeinfo /index.php/.well-known/nodeinfo 301
request_body {
max_size 10GB
}
}
# OnlyOffice Document Server
{{ subdomain_office }}.{{ domain }} {
reverse_proxy onlyoffice:80
request_body {
max_size 100MB
}
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# Excalidraw
{{ subdomain_draw }}.{{ domain }} {
reverse_proxy excalidraw:80
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# Obsidian
{{ subdomain_notes }}.{{ domain }} {
reverse_proxy obsidian:3000
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
# ===== TAILSCALE-ONLY SERVICES =====
# Homarr Dashboard
{{ subdomain_homarr }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy homarr:7575
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
# Dockhand Container Manager
{{ subdomain_dockhand }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy dockhand:3000
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
# Uptime Kuma Monitoring
{{ subdomain_uptime }}.{{ domain }} {
@tailscale {
remote_ip 100.64.0.0/10
}
handle @tailscale {
reverse_proxy uptime-kuma:3001
}
handle {
respond "Access Denied - Tailscale Required" 403
abort
}
}
{% if enable_public_status %}
# Public Status Page
status.{{ domain }} {
reverse_proxy uptime-kuma:3001/status
header {
Strict-Transport-Security "max-age=31536000"
-Server
}
}
{% endif %}

View File

@@ -0,0 +1,229 @@
services:
# ===== DATABASE LAYER =====
next-db:
image: docker.io/postgres:18
container_name: next-db
restart: unless-stopped
environment:
- POSTGRES_DB={{ db_name }}
- POSTGRES_USER={{ db_user }}
- POSTGRES_PASSWORD={{ db_password }}
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- pg_data:/var/lib/postgresql/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
interval: 10s
timeout: 5s
retries: 5
next-redis:
image: docker.io/redis:7-alpine
container_name: next-redis
restart: unless-stopped
command: redis-server --requirepass {{ redis_password }} --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ===== APPLICATION LAYER =====
next:
image: docker.io/nextcloud:latest
container_name: next
restart: unless-stopped
depends_on:
next-db:
condition: service_healthy
next-redis:
condition: service_healthy
mem_swappiness: -1
environment:
- POSTGRES_DB={{ db_name }}
- POSTGRES_USER={{ db_user }}
- POSTGRES_PASSWORD={{ db_password }}
- POSTGRES_HOST=next-db
- REDIS_HOST=next-redis
- REDIS_HOST_PASSWORD={{ redis_password }}
- NEXTCLOUD_TRUSTED_DOMAINS={{ subdomain_nextcloud }}.{{ domain }}
- NEXTCLOUD_ADMIN_USER={{ admin_user }}
- NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST={{ subdomain_nextcloud }}.{{ domain }}
- TRUSTED_PROXIES=caddy
- PHP_MEMORY_LIMIT=512M
- PHP_UPLOAD_LIMIT=10G
volumes:
- nextcloud_data:/var/www/html
- {{ deployment_dir }}/configs/nextcloud:/var/www/html/config:Z
- nextcloud_apps:/var/www/html/custom_apps
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/status.php"]
interval: 30s
timeout: 10s
retries: 3
onlyoffice:
image: docker.io/onlyoffice/documentserver:latest
container_name: onlyoffice
restart: unless-stopped
environment:
- JWT_ENABLED=false
- JWT_HEADER=Authorization
- JWT_IN_BODY=true
volumes:
- onlyoffice_data:/var/www/onlyoffice/Data
- onlyoffice_logs:/var/log/onlyoffice
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
excalidraw:
image: docker.io/excalidraw/excalidraw:latest
container_name: excalidraw
restart: unless-stopped
environment:
- NODE_ENV=production
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
restart: unless-stopped
security_opt:
- seccomp:unconfined
environment:
- PUID=1000
- PGID=1000
- TZ={{ timezone }}
volumes:
- {{ deployment_dir }}/data/obsidian/config:/config:z
- {{ deployment_dir }}/data/obsidian/vault:/vault:z
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
shm_size: "1gb"
# ===== INFRASTRUCTURE LAYER =====
caddy:
image: docker.io/caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- {{ deployment_dir }}/configs/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
environment:
- DOMAIN={{ domain }}
- EMAIL={{ user_email }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
homarr:
image: ghcr.io/homarr-labs/homarr:latest
container_name: homarr
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- {{ deployment_dir }}/data/homarr:/appdata
environment:
- SECRET_ENCRYPTION_KEY={{ homarr_secret }}
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
ports:
- "3003:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- dockhand_data:/app/data
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
restart: unless-stopped
volumes:
- uptime_kuma_data:/app/data
environment:
- TZ={{ timezone }}
networks:
- nextcloud_network
labels:
- "com.centurylinklabs.watchtower.enable=true"
watchtower:
image: containrrr/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_POLL_INTERVAL=86400
- WATCHTOWER_ROLLING_RESTART=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- TZ={{ timezone }}
networks:
- nextcloud_network
# ===== PERSISTENT STORAGE =====
volumes:
pg_data:
redis_data:
nextcloud_data:
nextcloud_apps:
onlyoffice_data:
onlyoffice_logs:
caddy_data:
caddy_config:
dockhand_data:
uptime_kuma_data:
# ===== NETWORKING =====
networks:
nextcloud_network:
name: nextcloud_network
driver: bridge

View File

@@ -0,0 +1,31 @@
# Environment Variables for Nextcloud Stack
# Generated by Ansible - DO NOT EDIT MANUALLY
# Database Configuration
DB_NAME={{ db_name }}
DB_USER={{ db_user }}
DB_PASSWORD={{ db_password }}
DB_HOST=next-db
# Redis Configuration
REDIS_PASSWORD={{ redis_password }}
# Nextcloud Admin
NEXTCLOUD_ADMIN_USER={{ admin_user }}
NEXTCLOUD_ADMIN_PASSWORD={{ admin_password }}
# Application Secrets
HOMARR_SECRET_KEY={{ homarr_secret }}
# Domain Configuration
DOMAIN={{ domain }}
SUBDOMAIN_NEXTCLOUD={{ subdomain_nextcloud }}
SUBDOMAIN_OFFICE={{ subdomain_office }}
# User Configuration
USER_EMAIL={{ user_email }}
TIMEZONE={{ timezone }}
# UIDs/GIDs
PUID=1000
PGID=1000

463
ansible/setup.sh Executable file
View File

@@ -0,0 +1,463 @@
#!/bin/bash
#
# Nextcloud Stack - Interactive Setup Script
# This script collects all configuration variables and generates Ansible inventory
#
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Header
clear
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ Nextcloud Stack - Ansible Deployment Setup ║${NC}"
echo -e "${CYAN}║ Version 1.0 ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "This script will guide you through configuring your Nextcloud"
echo "deployment. Information will be encrypted using Ansible Vault."
echo ""
# Arrays to store server data
declare -a SERVER_IPS
declare -a SERVER_HOSTNAMES
declare -a SERVER_DOMAINS
declare -a SERVER_SSH_USERS
declare -a SERVER_SSH_KEYS
# Global variables
ADMIN_USER=""
ADMIN_PASSWORD=""
USER_NAME=""
USER_EMAIL=""
TIMEZONE="Europe/Zurich"
INSTALL_RCLONE="y"
ENABLE_PUBLIC_STATUS="n"
ALERT_EMAIL=""
TAILSCALE_AUTH_KEY=""
# Subdomain defaults
SUBDOMAIN_NEXTCLOUD="cloud"
SUBDOMAIN_OFFICE="office"
SUBDOMAIN_DRAW="draw"
SUBDOMAIN_NOTES="notes"
SUBDOMAIN_HOMARR="home"
SUBDOMAIN_DOCKHAND="manage"
SUBDOMAIN_UPTIME="uptime"
# Database defaults
DB_NAME="nextcloud"
DB_USER="nextcloud"
# Generate random password
generate_password() {
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
}
# Validate IP address
validate_ip() {
local ip=$1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
return 0
else
return 1
fi
}
# Validate domain
validate_domain() {
local domain=$1
if [[ $domain =~ ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$ ]]; then
return 0
else
return 1
fi
}
# Server Configuration
echo -e "${GREEN}=== Server Configuration ===${NC}"
echo "Enter server details (press Enter without input to finish):"
echo ""
server_count=0
while true; do
((server_count++))
echo -e "${BLUE}Server $server_count:${NC}"
# IP Address
while true; do
read -p " IP address: " ip
if [[ -z "$ip" ]]; then
((server_count--))
break 2
fi
if validate_ip "$ip"; then
SERVER_IPS+=("$ip")
break
else
echo -e "${RED} Invalid IP address. Please try again.${NC}"
fi
done
# Hostname
read -p " Hostname [cloud$(printf "%02d" $server_count)]: " hostname
hostname=${hostname:-cloud$(printf "%02d" $server_count)}
SERVER_HOSTNAMES+=("$hostname")
# Domain
while true; do
read -p " Domain (e.g., example.com): " domain
if [[ -z "$domain" ]]; then
echo -e "${RED} Domain is required. Please try again.${NC}"
elif validate_domain "$domain"; then
SERVER_DOMAINS+=("$domain")
break
else
echo -e "${RED} Invalid domain format. Please try again.${NC}"
fi
done
# SSH User
read -p " SSH user [root]: " ssh_user
ssh_user=${ssh_user:-root}
SERVER_SSH_USERS+=("$ssh_user")
# SSH Key
read -p " SSH key path [~/.ssh/id_rsa]: " ssh_key
ssh_key=${ssh_key:-~/.ssh/id_rsa}
# Expand tilde
ssh_key="${ssh_key/#\~/$HOME}"
SERVER_SSH_KEYS+=("$ssh_key")
echo ""
done
if [[ ${#SERVER_IPS[@]} -eq 0 ]]; then
echo -e "${RED}Error: At least one server is required.${NC}"
exit 1
fi
echo -e "${GREEN}✓ Configured ${#SERVER_IPS[@]} server(s)${NC}"
echo ""
# User Information
echo -e "${GREEN}=== User Information ===${NC}"
read -p "Your name: " USER_NAME
while [[ -z "$USER_NAME" ]]; do
echo -e "${RED}Name is required.${NC}"
read -p "Your name: " USER_NAME
done
read -p "Your email: " USER_EMAIL
while [[ -z "$USER_EMAIL" ]] || [[ ! "$USER_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
echo -e "${RED}Valid email is required.${NC}"
read -p "Your email: " USER_EMAIL
done
read -p "Timezone [Europe/Zurich]: " TIMEZONE
TIMEZONE=${TIMEZONE:-Europe/Zurich}
echo ""
# Nextcloud Admin Account
echo -e "${GREEN}=== Nextcloud Admin Account ===${NC}"
read -p "Admin username [admin]: " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-admin}
while true; do
read -sp "Admin password: " ADMIN_PASSWORD
echo ""
read -sp "Confirm password: " ADMIN_PASSWORD_CONFIRM
echo ""
if [[ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]] && [[ ${#ADMIN_PASSWORD} -ge 8 ]]; then
break
elif [[ ${#ADMIN_PASSWORD} -lt 8 ]]; then
echo -e "${RED}Password must be at least 8 characters.${NC}"
else
echo -e "${RED}Passwords do not match. Please try again.${NC}"
fi
done
echo ""
# Subdomain Configuration
echo -e "${GREEN}=== Subdomain Configuration ===${NC}"
echo "Press Enter to use defaults shown in brackets"
read -p "Nextcloud subdomain [cloud]: " SUBDOMAIN_NEXTCLOUD
SUBDOMAIN_NEXTCLOUD=${SUBDOMAIN_NEXTCLOUD:-cloud}
read -p "OnlyOffice subdomain [office]: " SUBDOMAIN_OFFICE
SUBDOMAIN_OFFICE=${SUBDOMAIN_OFFICE:-office}
read -p "Excalidraw subdomain [draw]: " SUBDOMAIN_DRAW
SUBDOMAIN_DRAW=${SUBDOMAIN_DRAW:-draw}
read -p "Obsidian subdomain [notes]: " SUBDOMAIN_NOTES
SUBDOMAIN_NOTES=${SUBDOMAIN_NOTES:-notes}
read -p "Homarr subdomain [home]: " SUBDOMAIN_HOMARR
SUBDOMAIN_HOMARR=${SUBDOMAIN_HOMARR:-home}
read -p "Dockhand subdomain [manage]: " SUBDOMAIN_DOCKHAND
SUBDOMAIN_DOCKHAND=${SUBDOMAIN_DOCKHAND:-manage}
read -p "Uptime Kuma subdomain [uptime]: " SUBDOMAIN_UPTIME
SUBDOMAIN_UPTIME=${SUBDOMAIN_UPTIME:-uptime}
echo ""
# Database Configuration
echo -e "${GREEN}=== Database Configuration ===${NC}"
read -p "Database name [nextcloud]: " DB_NAME
DB_NAME=${DB_NAME:-nextcloud}
read -p "Database user [nextcloud]: " DB_USER
DB_USER=${DB_USER:-nextcloud}
echo "Generating secure passwords..."
DB_PASSWORD=$(generate_password)
REDIS_PASSWORD=$(generate_password)
HOMARR_SECRET=$(openssl rand -hex 32)
echo -e "${GREEN}✓ Passwords generated${NC}"
echo ""
# Tailscale Configuration
echo -e "${GREEN}=== Tailscale Configuration ===${NC}"
read -p "Install Tailscale? (y/n) [y]: " install_tailscale
install_tailscale=${install_tailscale:-y}
if [[ "$install_tailscale" == "y" ]]; then
read -p "Tailscale auth key (optional, press Enter to skip): " TAILSCALE_AUTH_KEY
if [[ -z "$TAILSCALE_AUTH_KEY" ]]; then
echo "Note: Tailscale will be installed but not activated."
echo " Activate manually with: sudo tailscale up"
fi
fi
echo ""
# Monitoring Configuration
echo -e "${GREEN}=== Monitoring Configuration ===${NC}"
read -p "Email for uptime alerts: " ALERT_EMAIL
while [[ -z "$ALERT_EMAIL" ]] || [[ ! "$ALERT_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; do
echo -e "${RED}Valid email is required.${NC}"
read -p "Email for uptime alerts: " ALERT_EMAIL
done
read -p "Enable public status page? (y/n) [n]: " ENABLE_PUBLIC_STATUS
ENABLE_PUBLIC_STATUS=${ENABLE_PUBLIC_STATUS:-n}
echo ""
# Backup Configuration
echo -e "${GREEN}=== Backup Configuration ===${NC}"
echo "Backup retention: 30 days (default)"
read -p "Install rclone for future remote backups? (y/n) [y]: " INSTALL_RCLONE
INSTALL_RCLONE=${INSTALL_RCLONE:-y}
if [[ "$INSTALL_RCLONE" == "y" ]]; then
echo "Note: rclone will be installed but not configured."
echo " Configure later with: rclone config"
fi
echo ""
# Ansible Vault Password
echo -e "${GREEN}=== Security ===${NC}"
echo "Create Ansible Vault password (will encrypt all secrets):"
while true; do
read -sp "Vault password: " VAULT_PASSWORD
echo ""
read -sp "Confirm: " VAULT_PASSWORD_CONFIRM
echo ""
if [[ "$VAULT_PASSWORD" == "$VAULT_PASSWORD_CONFIRM" ]] && [[ ${#VAULT_PASSWORD} -ge 8 ]]; then
break
elif [[ ${#VAULT_PASSWORD} -lt 8 ]]; then
echo -e "${RED}Password must be at least 8 characters.${NC}"
else
echo -e "${RED}Passwords do not match. Please try again.${NC}"
fi
done
echo ""
# Configuration Summary
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ CONFIGURATION SUMMARY ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Servers: ${#SERVER_IPS[@]}"
for i in "${!SERVER_IPS[@]}"; do
echo "${SERVER_IPS[$i]} (${SERVER_DOMAINS[$i]})"
done
echo ""
echo "Services (per server):"
echo " • Nextcloud: https://$SUBDOMAIN_NEXTCLOUD.<domain>"
echo " • OnlyOffice: https://$SUBDOMAIN_OFFICE.<domain>"
echo " • Excalidraw: https://$SUBDOMAIN_DRAW.<domain>"
echo " • Obsidian: https://$SUBDOMAIN_NOTES.<domain>"
echo " • Homarr: https://$SUBDOMAIN_HOMARR.<domain> (Tailscale)"
echo " • Dockhand: https://$SUBDOMAIN_DOCKHAND.<domain> (Tailscale)"
echo " • Uptime Kuma: https://$SUBDOMAIN_UPTIME.<domain> (Tailscale)"
echo ""
read -p "Proceed with these settings? (y/n): " confirm
if [[ "$confirm" != "y" ]]; then
echo "Setup cancelled."
exit 0
fi
echo ""
# Generate Configuration Files
echo "Generating configuration files..."
# Create inventory directory
mkdir -p inventory/group_vars/all
# Generate hosts.yml
cat >inventory/hosts.yml <<EOF
# Ansible Inventory - Generated by setup.sh
# Date: $(date)
all:
children:
nextcloud_servers:
hosts:
EOF
for i in "${!SERVER_IPS[@]}"; do
cat >>inventory/hosts.yml <<EOF
${SERVER_HOSTNAMES[$i]}:
ansible_host: ${SERVER_IPS[$i]}
ansible_user: ${SERVER_SSH_USERS[$i]}
ansible_ssh_private_key_file: ${SERVER_SSH_KEYS[$i]}
domain: ${SERVER_DOMAINS[$i]}
EOF
done
echo -e "${GREEN}✓ Created: inventory/hosts.yml${NC}"
# Generate vars.yml (public variables)
cat >inventory/group_vars/all/vars.yml <<EOF
# Public Variables - Generated by setup.sh
# Date: $(date)
# User Information
user_name: "$USER_NAME"
user_email: "$USER_EMAIL"
timezone: "$TIMEZONE"
# Admin Account
admin_user: "$ADMIN_USER"
# Subdomain Configuration
subdomain_nextcloud: "$SUBDOMAIN_NEXTCLOUD"
subdomain_office: "$SUBDOMAIN_OFFICE"
subdomain_draw: "$SUBDOMAIN_DRAW"
subdomain_notes: "$SUBDOMAIN_NOTES"
subdomain_homarr: "$SUBDOMAIN_HOMARR"
subdomain_dockhand: "$SUBDOMAIN_DOCKHAND"
subdomain_uptime: "$SUBDOMAIN_UPTIME"
# Database Configuration
db_name: "$DB_NAME"
db_user: "$DB_USER"
# Backup Configuration
backup_retention_days: 30
install_rclone: $INSTALL_RCLONE
# Monitoring
alert_email: "$ALERT_EMAIL"
enable_public_status: $ENABLE_PUBLIC_STATUS
# Deployment Settings
deployment_dir: "/opt/nextcloud-stack"
stack_name: "nextcloud"
# Docker Configuration
docker_compose_version: "v2"
EOF
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vars.yml${NC}"
# Generate vault.yml (encrypted secrets)
VAULT_CONTENT=$(
cat <<EOF
# Encrypted Secrets - Generated by setup.sh
# Date: $(date)
# Admin Password
admin_password: "$ADMIN_PASSWORD"
# Database Credentials
db_password: "$DB_PASSWORD"
redis_password: "$REDIS_PASSWORD"
# Application Secrets
homarr_secret: "$HOMARR_SECRET"
# Tailscale
tailscale_auth_key: "$TAILSCALE_AUTH_KEY"
EOF
)
# Save vault password to temporary file
echo "$VAULT_PASSWORD" >.vault_pass_temp
# Create encrypted vault
echo "$VAULT_CONTENT" | ansible-vault encrypt --vault-password-file=.vault_pass_temp --output=inventory/group_vars/all/vault.yml
# Clean up temp file
rm .vault_pass_temp
echo -e "${GREEN}✓ Created: inventory/group_vars/all/vault.yml (encrypted)${NC}"
echo ""
# Save vault password hint
echo "IMPORTANT: Save your vault password!"
echo "You will need it to run the playbook."
echo ""
echo "Vault password: **************** (hidden)"
echo ""
# Final Instructions
echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ READY TO DEPLOY! ║${NC}"
echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Next steps:"
echo ""
echo "1. Review configuration (optional):"
echo " ansible-inventory --list"
echo ""
echo "2. Test connectivity:"
echo " ansible all -m ping --ask-vault-pass"
echo ""
echo "3. Deploy stack:"
echo " ansible-playbook playbooks/site.yml --ask-vault-pass"
echo ""
echo " OR use Makefile:"
echo " make deploy"
echo ""
echo "Estimated deployment time: 30-45 minutes per server"
echo ""
echo -e "${YELLOW}WARNING: Ensure DNS records are configured before deploying!${NC}"
echo " Let's Encrypt will fail if DNS is not pointing correctly."
echo ""
echo "Required DNS records for each server:"
for i in "${!SERVER_DOMAINS[@]}"; do
echo " ${SERVER_DOMAINS[$i]}:"
echo " $SUBDOMAIN_NEXTCLOUD.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_OFFICE.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_DRAW.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_NOTES.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_HOMARR.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_DOCKHAND.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo " $SUBDOMAIN_UPTIME.${SERVER_DOMAINS[$i]}${SERVER_IPS[$i]}"
echo ""
done
echo ""
echo "Setup complete! 🎉"

View File

@@ -1,56 +1,47 @@
## name: Rosé Pine Moon
## author: mvllow
## license: MIT
## upstream: https://github.com/rose-pine/kitty/blob/main/dist/rose-pine-moon.conf
## blurb: All natural pine, faux fur and a bit of soho vibes for the classy minimalist
background #282828
foreground #d4be98
foreground #e0def4
background #232136
selection_foreground #e0def4
selection_background #44415a
selection_background #d4be98
selection_foreground #282828
cursor #56526e
cursor_text_color #e0def4
cursor #a89984
cursor_text_color background
url_color #c4a7e7
active_tab_background #282828
active_tab_foreground #d4be98
active_tab_font_style bold
inactive_tab_background #282828
inactive_tab_foreground #a89984
inactive_tab_font_style normal
active_tab_foreground #e0def4
active_tab_background #393552
inactive_tab_foreground #6e6a86
inactive_tab_background #232136
# Black
color0 #665c54
color8 #928374
active_border_color #3e8fb0
inactive_border_color #44415a
# Red
color1 #ea6962
color9 #ea6962
# black
color0 #393552
color8 #6e6a86
# Green
color2 #a9b665
color10 #a9b665
# red
color1 #eb6f92
color9 #eb6f92
# Yellow
color3 #e78a4e
color11 #d8a657
# green
color2 #3e8fb0
color10 #3e8fb0
# Blue
color4 #7daea3
color12 #7daea3
# yellow
color3 #f6c177
color11 #f6c177
# Magenta
color5 #d3869b
color13 #d3869b
# blue
color4 #9ccfd8
color12 #9ccfd8
# magenta
color5 #c4a7e7
color13 #c4a7e7
# cyan
color6 #ea9a97
color14 #ea9a97
# white
color7 #e0def4
color15 #e0def4
# Cyan
color6 #89b482
color14 #89b482
# White
color7 #d4be98
color15 #d4be98

View File

@@ -1,5 +1,5 @@
# BEGIN_KITTY_THEME
# Rosé Pine Moon
# Gruvbox Material
include current-theme.conf
# END_KITTY_THEME
#

View File

@@ -1,5 +1,5 @@
# BEGIN_KITTY_THEME
# Gruvbox Material
# Kanagawa
include current-theme.conf
# END_KITTY_THEME
#

View File

@@ -55,8 +55,8 @@ layout {
}
focus-ring {
width 2
active-color "#c4a7e7"
inactive-color "#a6adc8"
active-color "#7daea3"
inactive-color "#928374"
}
border {
off
@@ -80,6 +80,7 @@ spawn-at-startup "swaync"
// spawn-at-startup "kanshi"
spawn-at-startup "swww-daemon"
spawn-at-startup "xwayland-satellite"
spawn-at-startup "gammastep" "-l" "47.38:8.54" "-t" "6000:4000"
spawn-at-startup "kitty"
spawn-at-startup "librewolf"
spawn-at-startup "sh" "-c" "sleep 5 && exec swayidle -w timeout 300 hyprlock timeout 600 'niri msg action power-off-monitors' resume 'niri msg action power-on-monitors' timeout 3600 'systemctl suspend'"
@@ -102,12 +103,23 @@ match app-id=r#"^org\.keepassxc\.KeePassXC$"#
match app-id=r#"^org\.gnome\.World\.Secrets$"#
block-out-from "screen-capture"
}
// terminal window-rule
window-rule {
match app-id="^(zoom|us.zoom.Zoom)$"
exclude title="^.*Zoom Workplace"
match app-id="Bitwarden"
default-column-width {
proportion 0.6667
}
open-floating true
}
// terminal window-rule
window-rule {
match app-id="Zoom Workplace"
open-floating true
}
// window-rule {
// match app-id="^(zoom|us.zoom.Zoom)$"
// exclude title="^.*Zoom Workplace"
// open-floating true
// }
window-rule {
match at-startup=true app-id="kitty"
@@ -173,6 +185,13 @@ window-rule {
}
open-floating true
}
window-rule {
match app-id="floating_network-tui"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
geometry-corner-radius 5
clip-to-geometry true
@@ -221,6 +240,9 @@ binds {
Mod+Mod5+T hotkey-overlay-title="Open Wavemon" {
spawn "kitty" "--class" "floating_wavemon" "-e" "wavemon"
}
Mod+Mod5+Z hotkey-overlay-title="Open network-tui" {
spawn "kitty" "--class" "floating_network-tui" "-e" "network-tui"
}
// Brightness Controll
Mod+Ctrl+0 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"

View File

@@ -26,7 +26,7 @@ button:hover {
padding: 5px;
}
#workspaces button {
padding: 0 10px;
padding: 0 2px;
background-color: transparent;
color: @theme_fg_color;
border-radius: 10px;

View File

@@ -1,11 +1,10 @@
vim.cmd.colorscheme("rose-pine")
-- sonokai
-- rose-pine
-- onedark
-- nightfox
-- kanagawa
-- gruvbox-mat
-- everforest
-- dracula
-- carbonfox
-- tokyonight
-- vim.cmd.colorscheme("rose-pine")
-- vim.cmd.colorscheme("sonokai")
-- vim.cmd.colorscheme("one-dark")
-- vim.cmd.colorscheme("nightfox")
-- vim.cmd.colorscheme("kanagawa")
vim.cmd.colorscheme("gruvbox-material")
-- vim.cmd.colorscheme("everforest")
-- vim.cmd.colorscheme("dracula")
-- vim.cmd.colorscheme("carbonfox")
-- vim.cmd.colorscheme("tokyonight")

View File

@@ -6,13 +6,25 @@ return {
main = "ibl",
event = { "BufReadPost", "BufNewFile" },
config = function()
-- Define highlights BEFORE setup() is called
local function set_highlights()
vim.api.nvim_set_hl(0, "IblIndent", { fg = "#313244" })
vim.api.nvim_set_hl(0, "IblScope", { fg = "#5E81AC" })
end
set_highlights()
vim.api.nvim_create_autocmd("ColorScheme", {
pattern = "*",
callback = set_highlights,
})
require("ibl").setup({
indent = {
char = "",
},
scope = {
enabled = true,
char = "", -- Same character, different color
char = "",
show_start = true,
show_end = true,
},
@@ -26,18 +38,6 @@ return {
},
},
})
-- Very subtle gray for all indent lines
vim.api.nvim_set_hl(0, "IblIndent", { fg = "#313244" })
-- Soft accent for current scope (choose one):
-- Blue:
vim.api.nvim_set_hl(0, "IblScope", { fg = "#5E81AC" })
-- Or Purple:
-- vim.api.nvim_set_hl(0, "IblScope", { fg = "#B48EAD" })
-- Or Green:
-- vim.api.nvim_set_hl(0, "IblScope", { fg = "#A3BE8C" })
end,
},
}

View File

@@ -1 +0,0 @@
/home/liph/.config/wofi/themes/rose-pine-moon.css

View File

@@ -0,0 +1,62 @@
/* ============================================================
Wofi Theme — Gruvbox Material
Based on sainnhe/gruvbox-material (Dark Hard variant)
Structure mirrored from provided rose-pine template
============================================================ */
window {
margin: 0px;
background-color: #282828;
border-radius: 10px;
border: 2px solid #7daea3;
color: #d4be98;
font-family: "Monofur Nerd Font";
font-size: 20px;
}
#input {
margin: 5px;
border-radius: 10px;
border: none;
color: #d8a657;
background-color: #32302f;
}
#inner-box {
margin: 5px;
border: none;
background-color: #32302f;
color: #282828;
border-radius: 5px;
}
#outer-box {
margin: 15px;
border: none;
background-color: #282828;
}
#scroll {
margin: 0px;
border: none;
}
#text {
margin: 5px;
border: none;
color: #d4be98;
}
#entry:selected {
background-color: #7daea3;
color: #282828;
border-radius: 5px;
outline: none;
}
#entry:selected * {
background-color: #7daea3;
color: #282828;
border-radius: 5px;
outline: none;
}

View File

@@ -0,0 +1,62 @@
/* ============================================================
Wofi Theme — Gruvbox Material
Based on sainnhe/gruvbox-material (Dark Hard variant)
Structure mirrored from provided rose-pine template
============================================================ */
window {
margin: 0px;
background-color: #282828;
border-radius: 10px;
border: 2px solid #7daea3;
color: #d4be98;
font-family: "Monofur Nerd Font";
font-size: 20px;
}
#input {
margin: 5px;
border-radius: 10px;
border: none;
color: #d8a657;
background-color: #32302f;
}
#inner-box {
margin: 5px;
border: none;
background-color: #32302f;
color: #282828;
border-radius: 5px;
}
#outer-box {
margin: 15px;
border: none;
background-color: #282828;
}
#scroll {
margin: 0px;
border: none;
}
#text {
margin: 5px;
border: none;
color: #d4be98;
}
#entry:selected {
background-color: #7daea3;
color: #282828;
border-radius: 5px;
outline: none;
}
#entry:selected * {
background-color: #7daea3;
color: #282828;
border-radius: 5px;
outline: none;
}

View File

@@ -0,0 +1,125 @@
/* ============================================================
Wofi Theme — Kanagawa
Based on rebelot/kanagawa.nvim (Wave variant)
============================================================ */
* {
font-family: "JetBrains Mono", "Iosevka", monospace;
font-size: 13px;
transition:
background-color 0.1s ease,
color 0.1s ease;
}
/* ── Main window ── */
window {
background-color: #1f1f28;
border: 1px solid #2a2a37;
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.65);
}
/* ── Outer layout wrapper ── */
#outer-box {
background-color: transparent;
padding: 10px;
}
/* ── Search input wrapper ── */
#input {
background-color: #16161d;
color: #dcd7ba;
border: 1px solid #363646;
border-radius: 6px;
padding: 8px 12px;
margin-bottom: 8px;
caret-color: #e82424;
outline: none;
}
#input:focus {
border-color: #7e9cd8;
box-shadow: 0 0 0 2px rgba(126, 156, 216, 0.15);
}
#input image {
color: #54546d;
}
/* ── Scrollable results area ── */
#scroll {
background-color: transparent;
}
#inner-box {
background-color: transparent;
}
/* ── Individual entry row ── */
#entry {
background-color: transparent;
color: #dcd7ba;
border-radius: 5px;
padding: 7px 10px;
margin: 2px 0;
border: 1px solid transparent;
}
#entry:hover {
background-color: #2a2a37;
border-color: #363646;
}
#entry:selected,
#entry.selected {
background-color: #223249;
border-color: #7e9cd8;
color: #7e9cd8;
}
/* ── Entry image / icon ── */
#entry image {
margin-right: 8px;
color: #98bb6c;
}
#entry:selected image,
#entry.selected image {
color: #7e9cd8;
}
/* ── Entry text label ── */
#entry label {
color: inherit;
}
/* ── Kanagawa accent palette ── */
/* wave-red e82424 */
/* autumn-yel e6c384 */
/* spring-grn 98bb6c */
/* crystal-blu 7e9cd8 */
/* oni-violet 957fb8 */
/* dragon-org ffa066 */
/* spring-vio 938aa9 */
/* ── Scrollbar ── */
scrollbar {
width: 4px;
background: transparent;
}
scrollbar slider {
background-color: #363646;
border-radius: 2px;
min-height: 40px;
}
scrollbar slider:hover {
background-color: #7e9cd8;
}
/* ── No results message ── */
#no-results {
color: #54546d;
padding: 12px;
}

View File

@@ -0,0 +1,126 @@
/* ============================================================
Wofi Theme — Nightfox
Based on EdenEast/nightfox.nvim (Nightfox variant)
============================================================ */
* {
font-family: "JetBrains Mono", "Iosevka", monospace;
font-size: 13px;
transition:
background-color 0.1s ease,
color 0.1s ease;
}
/* ── Main window ── */
window {
background-color: #192330;
border: 1px solid #212e3f;
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
}
/* ── Outer layout wrapper ── */
#outer-box {
background-color: transparent;
padding: 10px;
}
/* ── Search input ── */
#input {
background-color: #131a24;
color: #cdcecf;
border: 1px solid #29394f;
border-radius: 6px;
padding: 8px 12px;
margin-bottom: 8px;
caret-color: #c94f6d;
outline: none;
}
#input:focus {
border-color: #719cd6;
box-shadow: 0 0 0 2px rgba(113, 156, 214, 0.15);
}
#input image {
color: #3b4261;
}
/* ── Scrollable results area ── */
#scroll {
background-color: transparent;
}
#inner-box {
background-color: transparent;
}
/* ── Individual entry row ── */
#entry {
background-color: transparent;
color: #cdcecf;
border-radius: 5px;
padding: 7px 10px;
margin: 2px 0;
border: 1px solid transparent;
}
#entry:hover {
background-color: #212e3f;
border-color: #29394f;
}
#entry:selected,
#entry.selected {
background-color: #1d3347;
border-color: #719cd6;
color: #719cd6;
}
/* ── Entry image / icon ── */
#entry image {
margin-right: 8px;
color: #81b29a;
}
#entry:selected image,
#entry.selected image {
color: #719cd6;
}
/* ── Entry text label ── */
#entry label {
color: inherit;
}
/* ── Nightfox accent palette ── */
/* red c94f6d */
/* orange f4a261 */
/* yellow dbc074 */
/* green 81b29a */
/* cyan 63cdcf */
/* blue 719cd6 */
/* magenta 9d79d6 */
/* pink d67ad2 */
/* ── Scrollbar ── */
scrollbar {
width: 4px;
background: transparent;
}
scrollbar slider {
background-color: #29394f;
border-radius: 2px;
min-height: 40px;
}
scrollbar slider:hover {
background-color: #719cd6;
}
/* ── No results message ── */
#no-results {
color: #3b4261;
padding: 12px;
}

View File

@@ -0,0 +1,125 @@
/* ============================================================
Wofi Theme — One Dark
Based on Atom's One Dark (navarasu/onedark.nvim)
============================================================ */
* {
font-family: "JetBrains Mono", "Iosevka", monospace;
font-size: 13px;
transition:
background-color 0.1s ease,
color 0.1s ease;
}
/* ── Main window ── */
window {
background-color: #282c34;
border: 1px solid #3e4451;
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
}
/* ── Outer layout wrapper ── */
#outer-box {
background-color: transparent;
padding: 10px;
}
/* ── Search input ── */
#input {
background-color: #21252b;
color: #abb2bf;
border: 1px solid #4b5263;
border-radius: 6px;
padding: 8px 12px;
margin-bottom: 8px;
caret-color: #e06c75;
outline: none;
}
#input:focus {
border-color: #61afef;
box-shadow: 0 0 0 2px rgba(97, 175, 239, 0.15);
}
#input image {
color: #5c6370;
}
/* ── Scrollable results area ── */
#scroll {
background-color: transparent;
}
#inner-box {
background-color: transparent;
}
/* ── Individual entry row ── */
#entry {
background-color: transparent;
color: #abb2bf;
border-radius: 5px;
padding: 7px 10px;
margin: 2px 0;
border: 1px solid transparent;
}
#entry:hover {
background-color: #2c313a;
border-color: #3e4451;
}
#entry:selected,
#entry.selected {
background-color: #2d3750;
border-color: #61afef;
color: #61afef;
}
/* ── Entry image / icon ── */
#entry image {
margin-right: 8px;
color: #98c379;
}
#entry:selected image,
#entry.selected image {
color: #61afef;
}
/* ── Entry text label ── */
#entry label {
color: inherit;
}
/* ── One Dark accent palette ── */
/* red e06c75 */
/* orange d19a66 */
/* yellow e5c07b */
/* green 98c379 */
/* cyan 56b6c2 */
/* blue 61afef */
/* magenta c678dd */
/* ── Scrollbar ── */
scrollbar {
width: 4px;
background: transparent;
}
scrollbar slider {
background-color: #4b5263;
border-radius: 2px;
min-height: 40px;
}
scrollbar slider:hover {
background-color: #61afef;
}
/* ── No results message ── */
#no-results {
color: #5c6370;
padding: 12px;
}

View File

@@ -0,0 +1,125 @@
/* ============================================================
Wofi Theme — Sonokai
Based on Sainnhe's Sonokai (Shusia variant)
============================================================ */
* {
font-family: "JetBrains Mono", "Iosevka", monospace;
font-size: 13px;
transition:
background-color 0.1s ease,
color 0.1s ease;
}
/* ── Main window ── */
window {
background-color: #2d2a2e;
border: 1px solid #403e41;
border-radius: 10px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
}
/* ── Outer layout wrapper ── */
#outer-box {
background-color: transparent;
padding: 10px;
}
/* ── Search input wrapper ── */
#input {
background-color: #221f22;
color: #e3e1e4;
border: 1px solid #5b595c;
border-radius: 6px;
padding: 8px 12px;
margin-bottom: 8px;
caret-color: #ff6188;
outline: none;
}
#input:focus {
border-color: #ff6188;
box-shadow: 0 0 0 2px rgba(255, 97, 136, 0.15);
}
#input image {
/* search icon */
color: #727072;
}
/* ── Scrollable results area ── */
#scroll {
background-color: transparent;
}
#inner-box {
background-color: transparent;
}
/* ── Individual entry row ── */
#entry {
background-color: transparent;
color: #e3e1e4;
border-radius: 5px;
padding: 7px 10px;
margin: 2px 0;
border: 1px solid transparent;
}
#entry:hover {
background-color: #3a3a3c;
border-color: #5b595c;
}
#entry:selected,
#entry.selected {
background-color: #403e41;
border-color: #ff6188;
color: #ff6188;
}
/* ── Entry image / icon ── */
#entry image {
margin-right: 8px;
color: #a9dc76;
}
#entry:selected image,
#entry.selected image {
color: #ff6188;
}
/* ── Entry text label ── */
#entry label {
color: inherit;
}
/* ── Custom property colours (usable in sub-text) ── */
/* accent-1 ff6188 pink */
/* accent-2 fc9867 orange */
/* accent-3 ffd866 yellow */
/* accent-4 a9dc76 green */
/* accent-5 78dce8 cyan */
/* accent-6 ab9df2 purple */
/* ── Scrollbar ── */
scrollbar {
width: 4px;
background: transparent;
}
scrollbar slider {
background-color: #5b595c;
border-radius: 2px;
min-height: 40px;
}
scrollbar slider:hover {
background-color: #ff6188;
}
/* ── No results message ── */
#no-results {
color: #727072;
padding: 12px;
}

View File

@@ -0,0 +1,30 @@
window {
background-color: rgb(26, 27, 38);
border-radius: 0.5em;
}
#input {
padding: 0.7em;
border-radius: 0.5em 0.5em 0 0;
background-color: #2d2e40;
color: #c0caf5;
border: none;
}
#text {
color: #c0caf5;
}
#entry {
padding: 0.7em;
color: #c0caf5;
}
#entry:selected {
background-color: #7aa2f7;
}
#text:selected {
color: #eee;
font-weight: bold;
}

Submodule yazi/.config/yazi/flavors/dracula.yazi added at 1b1a8d1083

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Chromium Oxide
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Mitchell Hanberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,45 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Example Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
```bash
ya pkg add Chromium-3-Oxide/everforest-medium
```
Or:
```bash
ya pack -a Chromium-3-Oxide/everforest-medium
```
## ⚙️ Usage
Add the these lines to your `theme.toml` configuration file to use it:
```toml
[flavor]
dark = "everforest-medium"
```
For Yazi versions before 0.4:
```toml
[flavor]
use = "everforest-medium"
```
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

View File

@@ -0,0 +1,229 @@
# vim:fileencoding=utf-8:foldmethod=marker
# : Manager {{{
[mgr]
cwd = { fg = "#7fbbb3" }
# Hovered
hovered = { reversed = true }
preview_hovered = { underline = true }
# Find
find_keyword = { fg = "#dbbc7f", bold = true, italic = true, underline = true }
find_position = { fg = "#d699b6", bg = "reset", bold = true, italic = true }
# Symlink
symlink_target = { italic = true }
# Marker
marker_copied = { fg = "#a7c080", bg = "#a7c080" }
marker_cut = { fg = "#e67e80", bg = "#e67e80" }
marker_marked = { fg = "#7fbbb3", bg = "#7fbbb3" }
marker_selected = { fg = "#dbbc7f", bg = "#dbbc7f" }
# Count
count_copied = { fg = "#343f44", bg = "#a7c080" }
count_cut = { fg = "#343f44", bg = "#e67e80" }
count_selected = { fg = "#343f44", bg = "#dbbc7f" }
# Border
border_symbol = "│"
border_style = { fg = "#4f585e" }
# Highlighting
syntect_theme = ""
# : }}}
# : Tabs {{{
[tabs]
active = { bg = "#7fbbb3", bold = true }
inactive = { fg = "#7fbbb3", bg = "#4f585e" }
# Separator
sep_inner = { open = "", close = "" }
sep_outer = { open = "", close = "" }
# : }}}
# : Mode {{{
[mode]
normal_main = { fg = "#3d484d", bg = "#a7c080", bold = true }
normal_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
# Select mode
select_main = { fg = "#3d484d", bg = "#e67e80", bold = true }
select_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
# Unset mode
unset_main = { fg = "#3d484d", bg = "#7fbbb3", bold = true }
unset_alt = { fg = "#7fbbb3", bg = "#4f585e", bold = true }
# : }}}
# : Status bar {{{
[status]
overall = {}
sep_left = { open = "", close = "" }
sep_right = { open = "", close = "" }
# Permissions
permissions_s = { fg = "#2d353b" }
permissions_t = { fg = "#a7c080" }
permissions_r = { fg = "#dbbc7f" }
permissions_w = { fg = "#e67e80" }
permissions_x = { fg = "#7fbbb3" }
# Progress
progress_label = { bold = true }
progress_normal = { fg = "#7fbbb3", bg = "#232a2e" }
progress_error = { fg = "#e67e80", bg = "#232a2e" }
# : }}}
# : Which {{{
[which]
cols = 3
mask = { bg = "#2d353b" }
cand = { fg = "#7fbbb3" }
rest = { fg = "#2d353b" }
desc = { fg = "#d699b6" }
separator = "  "
separator_style = { fg = "#2d353b" }
# : }}}
# : Confirmation {{{
[confirm]
border = { fg = "#7fbbb3" }
title = { fg = "#7fbbb3" }
body = {}
list = {}
btn_yes = { reversed = true }
btn_no = {}
btn_labels = [ " [Y]es ", " (N)o " ]
# : }}}
# : Spotter {{{
[spot]
border = { fg = "#7fbbb3" }
title = { fg = "#7fbbb3" }
# Table
tbl_col = { fg = "#7fbbb3" }
tbl_cell = { fg = "#dbbc7f", reversed = true }
# : }}}
# : Notification {{{
[notify]
title_info = { fg = "#a7c080" }
title_warn = { fg = "#dbbc7f" }
title_error = { fg = "#e67e80" }
# Icons
icon_info = ""
icon_warn = ""
icon_error = ""
# : }}}
# : Picker {{{
[pick]
border = { fg = "#7fbbb3" }
active = { fg = "#d699b6", bold = true }
inactive = {}
# : }}}
# : Input {{{
[input]
border = { fg = "#7fbbb3" }
title = {}
value = {}
selected = { reversed = true }
# : }}}
# : Completion {{{
[cmp]
border = { fg = "#7fbbb3" }
active = { reversed = true }
inactive = {}
# Icons
icon_file = ""
icon_folder = ""
icon_command = ""
# : }}}
# : Task manager {{{
[tasks]
border = { fg = "#7fbbb3" }
title = {}
hovered = { fg = "#d699b6", underline = true }
# : }}}
# : Help menu {{{
[help]
on = { fg = "#7fbbb3" }
run = { fg = "#d699b6" }
desc = {}
hovered = { reversed = true, bold = true }
footer = { fg = "#2d353b", bg = "#d3c6aa" }
# : }}}
# : File-specific styles {{{
[filetype]
rules = [
# Images
{ mime = "image/*", fg = "#7fbbb3" },
# Media
{ mime = "{audio,video}/*", fg = "#d699b6" },
# Archives
{ mime = "application/*zip", fg = "#e67e80" },
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "#e67e80" },
# Documents
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "#7fbbb3" },
# Fallback
{ name = "*", fg = "#83c092" },
{ name = "*/", fg = "#a7c080" }
]
# : }}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 - Matthew Dong
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,42 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Gruvbox Material Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
![gruvbox-material](./preview.png)
## 🎨 Installation
### Using package manager
```bash
ya pkg add matt-dong-123/gruvbox-material
```
### Manual install
```bash
# Linux/macOS
git clone https://github.com/your-username/gruvbox-material.yazi.git ~/.config/yazi/flavors/gruvbox-material.yazi
# Windows
git clone https://github.com/your-username/gruvbox-material.yazi.git %AppData%\yazi\config\flavors\gruvbox-material.yazi
```
## ⚙️ Usage
Add the these lines to your `theme.toml` configuration file to use it:
```toml
[flavor]
dark = "gruvbox-material"
```
## 📜 License
Check the [LICENSE](LICENSE) file for more details.

View File

@@ -0,0 +1,214 @@
# vim:fileencoding=utf-8:foldmethod=marker
# : Manager {{{
[mgr]
cwd = { fg = "#89b482" }
# Find
find_keyword = { fg = "#d8a657", bold = true, italic = true, underline = true }
find_position = { fg = "#d3869b", bg = "reset", bold = true, italic = true }
# Marker
marker_copied = { fg = "#a9b665", bg = "#a9b665" }
marker_cut = { fg = "#ea6962", bg = "#ea6962" }
marker_marked = { fg = "#89b482", bg = "#89b482" }
marker_selected = { fg = "#d8a657", bg = "#d8a657" }
# Count
count_copied = { fg = "#282828", bg = "#a9b665" }
count_cut = { fg = "#282828", bg = "#ea6962" }
count_selected = { fg = "#282828", bg = "#d8a657" }
# Border
border_symbol = "│"
border_style = { fg = "#928374" }
# : }}}
# : Tabs {{{
[tabs]
active = { fg = "#282828", bg = "#7daea3", bold = true }
inactive = { fg = "#7daea3", bg = "#32302f" }
# : }}}
# : Mode {{{
[mode]
normal_main = { fg = "#282828", bg = "#7daea3", bold = true }
normal_alt = { fg = "#7daea3", bg = "#32302f" }
# Select mode
select_main = { fg = "#282828", bg = "#89b482", bold = true }
select_alt = { fg = "#89b482", bg = "#32302f" }
# Unset mode
unset_main = { fg = "#282828", bg = "#ea6962", bold = true }
unset_alt = { fg = "#ea6962", bg = "#32302f" }
# : }}}
# : Status bar {{{
[status]
# Permissions
perm_sep = { fg = "#928374" }
perm_type = { fg = "#7daea3" }
perm_read = { fg = "#d8a657" }
perm_write = { fg = "#ea6962" }
perm_exec = { fg = "#a9b665" }
# Progress
progress_label = { fg = "#ddc7a1", bold = true }
progress_normal = { fg = "#a9b665", bg = "#45403d" }
progress_error = { fg = "#d8a657", bg = "#ea6962" }
# : }}}
# : Pick {{{
[pick]
border = { fg = "#7daea3" }
active = { fg = "#d3869b", bold = true }
inactive = {}
# : }}}
# : Input {{{
[input]
border = { fg = "#7daea3" }
title = {}
value = {}
selected = { reversed = true }
# : }}}
# : Completion {{{
[cmp]
border = { fg = "#7daea3" }
# : }}}
# : Tasks {{{
[tasks]
border = { fg = "#7daea3" }
title = {}
hovered = { fg = "#d3869b", bold = true }
# : }}}
# : Which {{{
[which]
mask = { bg = "#32302f" }
cand = { fg = "#89b482" }
rest = { fg = "#a89984" }
desc = { fg = "#d3869b" }
separator = "  "
separator_style = { fg = "#7c6f64" }
# : }}}
# : Help {{{
[help]
on = { fg = "#89b482" }
run = { fg = "#d3869b" }
hovered = { reversed = true, bold = true }
footer = { fg = "#32302f", bg = "#d4be98" }
# : }}}
# : Spotter {{{
[spot]
border = { fg = "#7daea3" }
title = { fg = "#7daea3" }
tbl_col = { fg = "#89b482" }
tbl_cell = { fg = "#d3869b", bg = "#45403d" }
# : }}}
# : Notification {{{
[notify]
title_info = { fg = "#a9b665" }
title_warn = { fg = "#d8a657" }
title_error = { fg = "#ea6962" }
# : }}}
# : File-specific styles {{{
[filetype]
rules = [
# Image
{ mime = "image/*", fg = "#89b482" },
# Media
{ mime = "{audio,video}/*", fg = "#d8a657" },
# Archive
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#d3869b" },
# Document
{ mime = "application/{pdf,doc,rtf}", fg = "#a9b665" },
# Virtual file system
{ mime = "vfs/{absent,stale}", fg = "#a89984" },
# Fallback
{ url = "*", fg = "#d4be98" },
{ url = "*/", fg = "#7daea3" },
]
# : }}}
[icon]
dirs = [
{ name = ".config", text = "", fg = "#d3869b" },
{ name = ".git", text = "", fg = "#89b482" },
{ name = ".github", text = "", fg = "#7daea3" },
{ name = ".npm", text = "", fg = "#7daea3" },
{ name = "Desktop", text = "", fg = "#89b482" },
{ name = "Development", text = "", fg = "#89b482" },
{ name = "Documents", text = "", fg = "#89b482" },
{ name = "Downloads", text = "", fg = "#89b482" },
{ name = "Library", text = "", fg = "#89b482" },
{ name = "Movies", text = "", fg = "#89b482" },
{ name = "Music", text = "", fg = "#89b482" },
{ name = "Pictures", text = "", fg = "#89b482" },
{ name = "Public", text = "", fg = "#89b482" },
{ name = "Videos", text = "", fg = "#89b482" },
]
conds = [
# Special files
{ if = "orphan", text = "", fg = "#d4be98" },
{ if = "link", text = "", fg = "#928374" },
{ if = "block", text = "", fg = "#ea6962" },
{ if = "char", text = "", fg = "#ea6962" },
{ if = "fifo", text = "", fg = "#ea6962" },
{ if = "sock", text = "", fg = "#ea6962" },
{ if = "sticky", text = "", fg = "#ea6962" },
{ if = "dummy", text = "", fg = "#ea6962" },
# Fallback
{ if = "dir", text = "", fg = "#7daea3" },
{ if = "exec", text = "", fg = "#a9b665" },
{ if = "!dir", text = "", fg = "#d4be98" },
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Dmitry Nefedov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Dmitry Nefedov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,36 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Kanagawa Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
```bash
ya pkg add dangooddd/kanagawa
```
## ⚙️ Usage
> [!Note]
> To achieve the look like in the screenshot you also need to install
> [full-border](https://github.com/yazi-rs/plugins/tree/main/full-border.yazi) plugin!
Add these lines to your `theme.toml` configuration file to use it:
```toml
[flavor]
dark = "kanagawa"
```
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

View File

@@ -0,0 +1,143 @@
## Color palette
## Check https://github.com/rebelot/kanagawa.nvim for more details
# sumiInk0 "#16161d" dark bg
# sumiInk3 "#1f1f28" bg
# sumiInk4 "#2a2a37"
# sumiInk6 "#54546d"
# fujiWhite "#dcd7ba" fg
# oldWhite "#c8c093" dark fg
# fujiGray "#727169"
# peachRed "#ff5d62"
# autumnRed "#c34043"
# crystalBlue "#7e9cd8"
# waveRed "#e46876"
# carpYellow "#e6c384"
# springGreen "#98bb6c"
# autumnGreen "#76946a"
# oniViolet "#957fb8"
# waveAqua1 "#6a9589"
# surimiOrange "#ffa066"
# waveAqua2 "#7aa89f"
[mgr]
marker_copied = { fg = "#98bb6c", bg = "#98bb6c" }
marker_cut = { fg = "#e46876", bg = "#e46876" }
marker_marked = { fg = "#957fb8", bg = "#957fb8" }
marker_selected = { fg = "#ffa066", bg = "#ffa066" }
cwd = { fg = "#e6c384" }
find_keyword = { fg = "#ffa066", bg = "#1f1f28" }
find_position = {}
count_copied = { fg = "#1f1f28", bg = "#98bb6c" }
count_cut = { fg = "#1f1f28", bg = "#e46876" }
count_selected = { fg = "#1f1f28", bg = "#ffa066" }
border_symbol = "│"
border_style = { fg = "#dcd7ba" }
[indicator]
parent = { reversed = true }
current = { reversed = true }
preview = { reversed = true }
[tabs]
active = { fg = "#1f1f28", bg = "#7e9cd8" }
inactive = { fg = "#7e9cd8", bg = "#2a2a37" }
sep_inner = { open = "", close = "" }
sep_outer = { open = "", close = "" }
[mode]
normal_main = { fg = "#1f1f28", bg = "#7e9cd8" }
normal_alt = { fg = "#7e9cd8", bg = "#2a2a37" }
select_main = { fg = "#1f1f28", bg = "#957fb8" }
select_alt = { fg = "#957fb8", bg = "#2a2a37" }
unset_main = { fg = "#1f1f28", bg = "#e6c384" }
unset_alt = { fg = "#e6c384", bg = "#2a2a37" }
[status]
sep_left = { open = "", close = "" }
sep_right = { open = "", close = "" }
overall = { fg = "#c8c093", bg = "#16161d" }
progress_label = { fg = "#7e9cd8", bold = true }
progress_normal = { fg = "#2a2a37", bg = "#1f1f28" }
progress_error = { fg = "#2a2a37", bg = "#1f1f28" }
perm_type = { fg = "#98bb6c" }
perm_read = { fg = "#e6c384" }
perm_write = { fg = "#ff5d62" }
perm_exec = { fg = "#7aa89f" }
perm_sep = { fg = "#957fb8" }
[pick]
border = { fg = "#7fb4ca" }
active = { fg = "#957fb8", bold = true }
inactive = {}
[input]
border = { fg = "#7fb4ca" }
title = {}
value = {}
selected = { reversed = true }
[completion]
border = { fg = "#7fb4ca" }
active = { reversed = true }
inactive = {}
[tasks]
border = { fg = "#7fb4ca" }
title = {}
hovered = { fg = "#957fb8" }
[which]
cols = 2
separator = " - "
separator_style = { fg = "#727169" }
mask = { bg = "#16161d" }
rest = { fg = "#727169" }
cand = { fg = "#7e9cd8" }
desc = { fg = "#54546d" }
[help]
on = { fg = "#7aa89f" }
run = { fg = "#957fb8" }
desc = {}
hovered = { reversed = true, bold = true }
footer = { fg = "#1f1f28", bg = "#dcd7ba" }
[notify]
title_info = { fg = "#98bb6c" }
title_warn = { fg = "#e6c384" }
title_error = { fg = "#ff5d62" }
[filetype]
rules = [
# images
{ mime = "image/*", fg = "#e6c384" },
# media
{ mime = "{audio,video}/*", fg = "#957fb8" },
# archives
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#e46876" },
# documents
{ mime = "application/{pdf,doc,rtf,vnd.*}", fg = "#6a9589" },
# broken links
{ url = "*", is = "orphan", fg = "#c34043" },
# executables
{ url = "*", is = "exec", fg = "#76946a" },
# fallback
{ url = "*", fg = "#dcd7ba" },
{ url = "*/", fg = "#7e9cd8" },
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@@ -0,0 +1,448 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>Kanagawa</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#1F1F28</string>
<key>caret</key>
<string>#C8C093</string>
<key>foreground</key>
<string>#DCD7BA</string>
<key>invisibles</key>
<string>#54546D</string>
<key>lineHighlight</key>
<string>#2D4F67</string>
<key>selection</key>
<string>#2D4F67</string>
<key>findHighlight</key>
<string>#2D4F67</string>
<key>selectionBorder</key>
<string>#222218</string>
<key>gutterForeground</key>
<string>#54546D</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#727169</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98BB6C</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#D27E99</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Built-in constant</string>
<key>scope</key>
<string>constant.language</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FFA066</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>User-defined constant</string>
<key>scope</key>
<string>constant.character, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E6C384</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>#E6C384</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Ruby's @variable</string>
<key>scope</key>
<string>variable.other.readwrite.instance</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#E6C384</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String interpolation</string>
<key>scope</key>
<string>constant.character.escaped, constant.character.escape, string source, string source.ruby</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#C0A36E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E46876</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#957FB8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage type</string>
<key>scope</key>
<string>storage.type</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#957FB8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class name</string>
<key>scope</key>
<string>entity.name.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7AA89F</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inherited class</string>
<key>scope</key>
<string>entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7AA89F</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function name</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#7E9CD8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function argument</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#b8b4d0</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag name</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#7FB4CA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#E6C384</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library function</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#7FB4CA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library constant</string>
<key>scope</key>
<string>support.constant</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#7FB4CA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library class/type</string>
<key>scope</key>
<string>support.type, support.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7AA89F</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library variable</string>
<key>scope</key>
<string>support.other.variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FFA066</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string/>
<key>foreground</key>
<string>#FF5D62</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#717C7C</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>JSON String</string>
<key>scope</key>
<string>meta.structure.dictionary.json string.quoted.double.json</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#957FB8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>diff.header</string>
<key>scope</key>
<string>meta.diff, meta.diff.header</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7E9CD8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>diff.deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#43242B</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>diff.inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#2B3328</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>diff.changed</string>
<key>scope</key>
<string>markup.changed</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#49443C</string>
</dict>
</dict>
<dict>
<key>scope</key>
<string>constant.numeric.line-number.find-in-files - match</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#54546D</string>
</dict>
</dict>
<dict>
<key>scope</key>
<string>entity.name.filename</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#C8C093</string>
</dict>
</dict>
<dict>
<key>scope</key>
<string>message.error</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E82424</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>JSON Punctuation</string>
<key>scope</key>
<string>punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#9CABCA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>JSON Structure</string>
<key>scope</key>
<string>meta.structure.dictionary.json string.quoted.double.json</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#957FB8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>JSON String</string>
<key>scope</key>
<string>meta.structure.dictionary.value.json string.quoted.double.json</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#ffffff</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape Characters</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FF5D62</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Regular Expressions</string>
<key>scope</key>
<string>string.regexp</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E6C384</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>592FC036-6BB7-4676-A2F5-2894D48C8E33</string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>semanticClass</key>
<string>theme.dark.kanagawa</string>
</dict>
</plist>

View File

@@ -0,0 +1,37 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Monokai flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
```sh
ya pkg add malick-tammal/monokai
```
## ⚙️ Usage
Add it to your `theme.toml` as dark flavor:
```toml
[flavor]
dark = "monokai"
```
Make sure your `theme.toml` doesn't contain anything other than `[flavor]`, unless you want to override certain styles of this flavor.
See the [Yazi flavor documentation](https://yazi-rs.github.io/docs/flavors/overview) for more details.
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

View File

@@ -0,0 +1,182 @@
#----------------------------------------------------------
#-- HACK: Monokai
#----------------------------------------------------------
[mgr]
cwd = { fg = "#78DCE8" }
# Hovered
hovered = { reversed = true }
preview_hovered = { underline = true }
# Find
find_symbol = " Finding: "
find_keyword = { fg = "#FF6188", bold = true, italic = true, underline = true }
find_position = { fg = "#AB9DF2", bg = "reset", bold = true, italic = true }
# Symlink
symlink_target = { fg = "#78DCE8", italic = true }
# Marker
marker_copied = { fg = "#A9DC76", bg = "#A9DC76" }
marker_cut = { fg = "lightred", bg = "lightred" }
marker_marked = { fg = "#AB9DF2", bg = "#AB9DF2" }
marker_selected = { fg = "#FF6188", bg = "#FF6188" }
# Count
count_copied = { fg = "#221F22", bg = "#A9DC76" }
count_cut = { fg = "#221F22", bg = "#FF6188" }
count_selected = { fg = "#221F22", bg = "#FFD866" }
# Border
border_symbol = "│"
border_style = { fg = "#727072" }
# Tabs
[tabs]
active = { fg = "#221F22", bg = "#FFD866", bold = true }
inactive = { fg = "#FFD866", bg = "#221F22" }
# Separator
sep_inner = { open = "", close = "" }
sep_outer = { open = "", close = "" }
# Modes
[mode]
# Normal mode
normal_main = { fg = "#2D2A2E", bg = "#FFD866", bold = true }
normal_alt = { fg = "#FFD866", bg = "#403E41" }
# Select mode
select_main = { fg = "#2D2A2E", bg = "#AB9DF2", bold = true }
select_alt = { fg = "#AB9DF2", bg = "#403E41" }
# Unset mode
unset_main = { bg = "#FF6188", bold = true }
unset_alt = { fg = "#FF6188", bg = "gray" }
# Status bar
[status]
overall = {}
sep_left = { open = "", close = "" }
sep_right = { open = "", close = "" }
# Permissions
perm_sep = { fg = "#727072" }
perm_type = { fg = "#A9DC76" }
perm_read = { fg = "#FFD866" }
perm_write = { fg = "#FF6188" }
perm_exec = { fg = "#78DCE8" }
# Progress
progress_label = { bold = true }
progress_normal = { fg = "#78DCE8", bg = "#2D2A2E" }
progress_error = { fg = "#FF6188", bg = "#2D2A2E" }
# Which
[which]
cols = 3
mask = { bg = "#2D2A2E" }
cand = { fg = "lightcyan" }
rest = { fg = "#727072" }
desc = { fg = "lightmagenta" }
separator = "  "
separator_style = { fg = "#727072" }
# Confirmation
[confirm]
border = { fg = "#FFD866" }
title = { fg = "#FFD866" }
content = {}
list = {}
btn_yes = { reversed = true }
btn_no = {}
btn_labels = [" [Y]es ", " (N)o "]
# Spotter
[spot]
border = { fg = "#78DCE8" }
title = { fg = "#78DCE8" }
# Table
tbl_col = { fg = "#78DCE8" }
tbl_cell = { fg = "#FFD866", reversed = true }
# Norifications
[notify]
title_info = { fg = "#A9DC76" }
title_warn = { fg = "#FFD866" }
title_error = { fg = "#FF6188" }
# Icons
icon_info = ""
icon_warn = ""
icon_error = ""
# Picker
[pick]
border = { fg = "#78DCE8" }
active = { fg = "#AB9DF2", bold = true }
inactive = {}
# Input
[input]
border = { fg = "#78DCE8" }
title = {}
value = {}
selected = { reversed = true }
# Completion
[cmp]
border = { fg = "#78DCE8" }
active = { reversed = true }
inactive = {}
# Icons
icon_file = ""
icon_folder = ""
icon_command = ""
# : Task manager {{{
[tasks]
border = { fg = "#78DCE8" }
title = {}
hovered = { fg = "#AB9DF2", underline = true }
# Help menu
[help]
on = { fg = "#78DCE8" }
run = { fg = "#AB9DF2" }
desc = {}
hovered = { reversed = true, bold = true }
footer = { fg = "#221F22", bg = "#FDFFF1" }
# File-specific styles
[filetype]
rules = [
# Images
{ mime = "image/*", fg = "#FFD866" },
# Media
{ mime = "{audio,video}/*", fg = "#A9DC76" },
# Archives
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#FF6188" },
# Documents
{ mime = "application/{pdf,doc,rtf}", fg = "#78DCE8" },
# Empty files
{ mime = "inode/empty", fg = "#FF6188" },
# Special files
{ url = "*", is = "orphan", bg = "#FF6188" },
{ url = "*", is = "exec", fg = "#A9DC76" },
# Dummy files
{ url = "*", is = "dummy", bg = "#FF6188" },
{ url = "*/", is = "dummy", bg = "#FF6188" },
# Fallback
{ url = "*/", fg = "#FC9767", bold = true },
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 KiB

View File

@@ -0,0 +1,300 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>gutterSettings</key>
<dict>
<key>background</key>
<string>#49483E</string>
<key>divider</key>
<string>#75715E</string>
<key>foreground</key>
<string>#75715E</string>
</dict>
<key>name</key>
<string>Monokai</string>
<key>semanticClass</key>
<string>theme.dark.monokai</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#272822</string>
<key>caret</key>
<string>#F8F8F0</string>
<key>foreground</key>
<string>#F8F8F2</string>
<key>invisibles</key>
<string>#49483E</string>
<key>lineHighlight</key>
<string>#49483E</string>
<key>selection</key>
<string>#49483E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#75715E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E6DB74</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AE81FF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Built-in constant</string>
<key>scope</key>
<string>constant.language</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AE81FF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>User-defined constant</string>
<key>scope</key>
<string>constant.character, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AE81FF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#F92672</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#F92672</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage type</string>
<key>scope</key>
<string>storage.type</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#66D9EF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class name</string>
<key>scope</key>
<string>entity.name.class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>underline</string>
<key>foreground</key>
<string>#A6E22E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inherited class</string>
<key>scope</key>
<string>entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic underline</string>
<key>foreground</key>
<string>#A6E22E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function name</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#A6E22E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function argument</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#FD971F</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag name</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#F92672</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#A6E22E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library function</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#66D9EF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library constant</string>
<key>scope</key>
<string>support.constant</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#66D9EF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library class/type</string>
<key>scope</key>
<string>support.type, support.class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#66D9EF</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library variable</string>
<key>scope</key>
<string>support.other.variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#F92672</string>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#F8F8F0</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#AE81FF</string>
<key>foreground</key>
<string>#F8F8F0</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>D8D5E82E-3D5B-46B5-B38E-8C841C21347D</string>
</dict>
</plist>

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 - sxyazi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Himanshu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,47 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Tokyo Night Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
### Using package manager
```bash
ya pkg add BennyOe/tokyo-night
```
### Manual install
```bash
# Linux/macOS
git clone https://github.com/BennyOe/tokyo-night.yazi.git ~/.config/yazi/flavors/tokyo-night.yazi
# Windows
git clone https://github.com/BennyOe/tokyo-night.yazi.git %AppData%\yazi\config\flavors\tokyo-night.yazi
```
## ⚙️ Usage
Add the these lines to your `theme.toml` configuration file to use it:
```toml
[flavor]
use = "tokyo-night"
# For Yazi 0.4 and above
dark = "tokyo-night"
```
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

View File

@@ -0,0 +1,201 @@
# vim:fileencoding=utf-8:foldmethod=marker
# : Manager {{{
[mgr]
cwd = { fg = "#7aa2f7" }
# Find
find_keyword = { fg = "#f7768e", bold = true, italic = true, underline = true }
find_position = { fg = "#bb9af7", bg = "reset", bold = true, italic = true }
# Marker
marker_copied = { fg = "#9ece6a", bg = "#9ece6a" }
marker_cut = { fg = "#e0af68", bg = "#f7768e" }
marker_marked = { fg = "#7aa2f7", bg = "#7dcfff" }
marker_selected = { fg = "#e0af68", bg = "#e0af68" }
# Count
count_copied = { fg = "#1a1b26", bg = "#9ece6a" }
count_cut = { fg = "#1a1b26", bg = "#e0af68" }
count_selected = { fg = "#1a1b26", bg = "#7aa2f7" }
# Border
border_symbol = "│"
border_style = { fg = "#414868" }
# : }}}
# : Tabs {{{
[tabs]
active = { fg = "#292e42", bg = "#7aa2f7", bold = true }
inactive = { fg = "#7aa2f7", bg = "#292e42" }
# : }}}
# : Mode {{{
[mode]
normal_main = { fg = "#292e42", bg = "#7aa2f7", bold = true }
normal_alt = { fg = "#7aa2f7", bg = "#292e42" }
# Select mode
select_main = { fg = "#292e42", bg = "#9ece6a", bold = true }
select_alt = { fg = "#7aa2f7", bg = "#292e42" }
# Unset mode
unset_main = { fg = "#292e42", bg = "#bb9af7", bold = true }
unset_alt = { fg = "#7aa2f7", bg = "#292e42" }
# : }}}
# : Status bar {{{
[status]
overall = { fg = "#7aa2f7" }
sep_left = { open = "", close = "" }
sep_right = { open = "", close = "" }
# Progress
progress_label = { fg = "#292e42", bold = true }
progress_normal = { fg = "#7aa2f7", bg = "#292e42" }
progress_error = { fg = "#f7768e", bg = "#292e42" }
# Permissions
perm_sep = { fg = "#7aa2f7" }
perm_type = { fg = "#9ece6a" }
perm_read = { fg = "#e0af68" }
perm_write = { fg = "#f7768e" }
perm_exec = { fg = "#bb9af7" }
# : }}}
# : Pick {{{
[pick]
border = { fg = "#7aa2f7" }
active = { fg = "#bb9af7", bold = true }
inactive = {}
# : }}}
# : Input {{{
[input]
border = { fg = "#7aa2f7" }
title = {}
value = {}
selected = { reversed = true }
# : }}}
# : Completion {{{
[cmp]
border = { fg = "#7aa2f7" }
# : }}}
# : Tasks {{{
[tasks]
border = { fg = "#7aa2f7" }
title = {}
hovered = { fg = "#bb9af7", underline = true }
# : }}}
# : Which {{{
[which]
mask = { bg = "#414868" }
cand = { fg = "#9ece6a" }
rest = { fg = "#a9b1d6" }
desc = { fg = "#bb9af7" }
separator = "  "
separator_style = { fg = "#626880" }
# : }}}
# : Help {{{
[help]
on = { fg = "#9ece6a" }
run = { fg = "#bb9af7" }
hovered = { reversed = true, bold = true }
footer = { fg = "#1a1b26", bg = "#a9b1d6" }
# : }}}
# : Spotter {{{
[spot]
border = { fg = "#7aa2f7" }
title = { fg = "#7aa2f7" }
tbl_col = { fg = "#9ece6a" }
tbl_cell = { fg = "#bb9af7", bg = "#292e42" }
# : }}}
# : Notify {{{
[notify]
title_info = { fg = "#9ece6a" }
title_warn = { fg = "#f7768e" }
title_error = { fg = "#e0af68" }
# : }}}
# : File-specific styles {{{
[filetype]
rules = [
# Images
{ mime = "image/*", fg = "#e0af68" },
# Media
{ mime = "video/*", fg = "#f7768e" },
{ mime = "audio/*", fg = "#f7768e" },
# Archives
{ mime = "application/zip", fg = "#bb9af7" },
{ mime = "application/x-tar", fg = "#bb9af7" },
{ mime = "application/x-bzip*", fg = "#bb9af7" },
{ mime = "application/x-bzip2", fg = "#bb9af7" },
{ mime = "application/x-7z-compressed", fg = "#bb9af7" },
{ mime = "application/x-rar", fg = "#bb9af7" },
{ mime = "application/x-xz", fg = "#bb9af7" },
# Documents
{ mime = "application/doc", fg = "#9ece6a" },
{ mime = "application/epub+zip", fg = "#9ece6a" },
{ mime = "application/pdf", fg = "#9ece6a" },
{ mime = "application/rtf", fg = "#9ece6a" },
{ mime = "application/vnd.*", fg = "#9ece6a" },
# Special files
{ mime = "*", is = "orphan", fg = "#f29cb4", bg = "#93000a" },
{ mime = "application/*exec*", fg = "#f7768e" },
# Fallback
{ url = "*", fg = "#c6d0f5" },
{ url = "*/", fg = "#7aa2f7" },
]
# : }}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,93 +1,98 @@
-- Change this to switch themes
local THEME = "rose_pine"
-- local THEME = "kanagawa"
-- local THEME = "catppuccin-mocha"
-- local THEME = "everforest-medium"
local THEME = "gruvbox-material"
-- local THEME = "tokyo-night"
-- local THEME = "rose_pine-moon"
local theme_colors = {
rose_pine = {
primary = "#c4a7e7", -- iris/mauve (purple-like color from rose-pine palette)
secondary = "#6e6a86", -- muted/subtle (comparable to comment/gray colors)
yanked = "#9ccfd8", -- foam (cyan-like accent color)
cut = "#eb6f92", -- love (red-like accent color)
},
carbonfox = {
primary = "#78a9ff",
secondary = "#525252",
yanked = "#3ddbd9",
cut = "#ff7eb6",
},
kanagawa = {
primary = "#7e9cd8",
secondary = "#54546d",
yanked = "#7aa89f",
cut = "#e82424",
},
sonokai = {
primary = "#76cce0",
secondary = "#7f8490",
yanked = "#fc5d7c",
cut = "#f85e84",
},
catppuccin = {
primary = "#89b4fa",
secondary = "#6c7086",
yanked = "#94e2d5",
cut = "#f38ba8",
},
dracula = {
primary = "#bd93f9", -- purple
secondary = "#6272a4", -- comment
yanked = "#8be9fd", -- cyan
cut = "#ff5555", -- red
},
everforest = {
primary = "#7fbbb3", -- aqua
secondary = "#859289", -- gray
yanked = "#a7c080", -- green
cut = "#e67e80", -- red
},
gruvbox = {
primary = "#7daea3", -- aqua
secondary = "#7c6f64", -- gray
yanked = "#a9b665", -- green
cut = "#ea6962", -- red
},
nightfox = {
primary = "#719cd6", -- blue
secondary = "#738091", -- comment
yanked = "#63cdcf", -- cyan
cut = "#c94f6d", -- red
},
onedark = {
primary = "#61afef", -- blue
secondary = "#5c6370", -- gray
yanked = "#56b6c2", -- cyan
cut = "#e06c75", -- red
},
}
-- local theme_colors = {
-- rose_pine = {
-- primary = "#c4a7e7", -- iris/mauve (purple-like color from rose-pine palette)
-- secondary = "#6e6a86", -- muted/subtle (comparable to comment/gray colors)
-- yanked = "#9ccfd8", -- foam (cyan-like accent color)
-- cut = "#eb6f92", -- love (red-like accent color)
-- },
-- carbonfox = {
-- primary = "#78a9ff",
-- secondary = "#525252",
-- yanked = "#3ddbd9",
-- cut = "#ff7eb6",
-- },
-- kanagawa = {
-- primary = "#7e9cd8",
-- secondary = "#54546d",
-- yanked = "#7aa89f",
-- cut = "#e82424",
-- },
-- sonokai = {
-- primary = "#76cce0",
-- secondary = "#7f8490",
-- yanked = "#fc5d7c",
-- cut = "#f85e84",
-- },
-- catppuccin = {
-- primary = "#89b4fa",
-- secondary = "#6c7086",
-- yanked = "#94e2d5",
-- cut = "#f38ba8",
-- },
-- dracula = {
-- primary = "#bd93f9", -- purple
-- secondary = "#6272a4", -- comment
-- yanked = "#8be9fd", -- cyan
-- cut = "#ff5555", -- red
-- },
-- everforest = {
-- primary = "#7fbbb3", -- aqua
-- secondary = "#859289", -- gray
-- yanked = "#a7c080", -- green
-- cut = "#e67e80", -- red
-- },
-- gruvbox = {
-- primary = "#7daea3", -- aqua
-- secondary = "#7c6f64", -- gray
-- yanked = "#a9b665", -- green
-- cut = "#ea6962", -- red
-- },
-- nightfox = {
-- primary = "#719cd6", -- blue
-- secondary = "#738091", -- comment
-- yanked = "#63cdcf", -- cyan
-- cut = "#c94f6d", -- red
-- },
-- onedark = {
-- primary = "#61afef", -- blue
-- secondary = "#5c6370", -- gray
-- yanked = "#56b6c2", -- cyan
-- cut = "#e06c75", -- red
-- },
-- }
local c = theme_colors[THEME]
-- eocal c = theme_colors[THEME]
require("yaziline"):setup({
color = c.primary,
secondary_color = c.secondary,
default_files_color = "darkgray",
selected_files_color = "white",
yanked_files_color = c.yanked,
cut_files_color = c.cut,
separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
separator_open = "",
separator_close = "",
separator_open_thin = "",
separator_close_thin = "",
separator_head = "",
separator_tail = "",
-- require("yaziline"):setup({
-- color = c.primary,
-- secondary_color = c.secondary,
-- default_files_color = "darkgray",
-- selected_files_color = "white",
-- yanked_files_color = c.yanked,
-- cut_files_color = c.cut,
-- separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
-- separator_open = "",
-- separator_close = "",
-- separator_open_thin = "",
-- separator_close_thin = "",
-- separator_head = "",
-- separator_tail = "",
select_symbol = "",
yank_symbol = "󰆐",
-- select_symbol = "",
-- yank_symbol = "󰆐",
filename_max_length = 24, -- truncate when filename > 24
filename_truncate_length = 6, -- leave 6 chars on both sides
filename_truncate_separator = "...",
})
-- filename_max_length = 24, -- truncate when filename > 24
-- filename_truncate_length = 6, -- leave 6 chars on both sides
-- filename_truncate_separator = "...",
-- })
require("recycle-bin"):setup({
trash_dir = "~/.local/share/Trash/",

View File

@@ -3,12 +3,12 @@ pwd = "osc7"
version = 3
[palette]
gb_bright_path = "#9ccdf8"
gb_bright_git = "#f6c177"
gb_bright_path = "#7daea3"
gb_bright_git = "#d8a657"
gb_dark0_hard = "#3e7fb0"
gb_faded_green = "#79740e"
gb_faded_red = "#ea9a97"
gb_text = "#c4a7e7"
gb_text = "#d3869b"
[var]
user = ""
@@ -21,14 +21,14 @@ alignment = "left"
[[blocks.segments]]
template = '{{ .HostName }} '
foreground = 'p:gb_text'
foreground = 'magenta'
background = 'transparent'
type = 'text'
style = 'plain'
[[blocks.segments]]
template = '{{ .UserName }} '
foreground = 'p:gb_text'
foreground = 'magenta'
background = 'transparent'
type = 'text'
style = 'plain'
@@ -38,7 +38,7 @@ style = 'plain'
[[blocks.segments]]
template = "{{ .Path }} "
foreground = "p:gb_bright_path"
foreground = "blue"
background = "transparent"
type = "path"
style = "plain"
@@ -49,7 +49,7 @@ enable_hyperlink = true
[[blocks.segments]]
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
foreground = "p:gb_bright_git"
foreground = "yellow"
background = "transparent"
type = "git"
style = "plain"

View File

@@ -0,0 +1,103 @@
"$schema" = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json"
pwd = "osc7"
version = 3
[palette]
gb_bright_path = "#9ccdf8"
gb_bright_git = "#f6c177"
gb_dark0_hard = "#3e7fb0"
gb_faded_green = "#79740e"
gb_faded_red = "#ea9a97"
gb_text = "#c4a7e7"
[var]
user = ""
ssh = "🌐"
# LEFT SIDE PROMPT
[[blocks]]
type = "prompt"
alignment = "left"
[[blocks.segments]]
template = '{{ .HostName }} '
foreground = 'p:gb_text'
background = 'transparent'
type = 'text'
style = 'plain'
[[blocks.segments]]
template = '{{ .UserName }} '
foreground = 'p:gb_text'
background = 'transparent'
type = 'text'
style = 'plain'
# [blocks.segments.properties]
# cache_duration = "none"
[[blocks.segments]]
template = "{{ .Path }} "
foreground = "p:gb_bright_path"
background = "transparent"
type = "path"
style = "plain"
[blocks.segments.properties]
style = "full"
enable_hyperlink = true
[[blocks.segments]]
template = ' {{ .HEAD }}{{ if or (.Working.Changed) (.Staging.Changed) }}*{{ end }} <cyan>{{ if gt .Behind 0 }}⇣{{ end }}{{ if gt .Ahead 0 }}⇡{{ end }}</>'
foreground = "p:gb_bright_git"
background = "transparent"
type = "git"
style = "plain"
[blocks.segments.properties]
fetch_status = true
[blocks.segments.properties.status_formats]
Added = "+%d"
Deleted = "-%d"
Modified = "~%d"
Untracked = "?%d"
# RIGHT SIDE PROMPT
[[blocks]]
type = "rprompt"
alignment = "right"
[[blocks.segments]]
template = " {{ .Full }}"
foreground = "p:gb_faded_green"
background = "transparent"
type = "node"
style = "plain"
[blocks.segments.properties]
display_mode = "files"
[[blocks.segments]]
template = " {{ if .Venv }}({{ .Venv }}) {{ end }}{{ .Full }}"
foreground = "p:gb_faded_green"
background = "transparent"
type = "python"
style = "plain"
[blocks.segments.properties]
display_mode = "always"
fetch_virtual_env = true
# NEWLINE FOR INPUT
[[blocks]]
type = "prompt"
alignment = "left"
newline = true
[[blocks.segments]]
template = " "
foreground = "p:gb_bright_aqua"
background = "transparent"
type = "text"
style = "plain"

View File

@@ -142,3 +142,33 @@ hash = "b951d27afe197d154f6da9683b46c5f8"
use = "Mintass/rose-pine-moon"
rev = "94385fe"
hash = "edbe0dfb5db8ff37281dba62adc7e750"
[[flavor.deps]]
use = "Chromium-3-Oxide/everforest-medium"
rev = "0158f0f"
hash = "a7c36abf0b13289aceb1c20a17c6a63e"
[[flavor.deps]]
use = "bennyyip/gruvbox-dark"
rev = "619fdc5"
hash = "d4c7003ec6dc4efd5d3a050d5c2f2575"
[[flavor.deps]]
use = "BennyOe/tokyo-night"
rev = "8e6296f"
hash = "d9da921c3bd37ba32ee27a8a7a40461f"
[[flavor.deps]]
use = "dangooddd/kanagawa"
rev = "04985d1"
hash = "c69a63981e254a3a06a11c2bf0e6af1d"
[[flavor.deps]]
use = "matt-dong-123/gruvbox-material"
rev = "6c36491"
hash = "564f2335540ee2c440c3b671bc28ac60"
[[flavor.deps]]
use = "malick-tammal/monokai"
rev = "e59f6cb"
hash = "dcb449d46e30dc020e4b7b59202d2448"

View File

@@ -12,7 +12,7 @@ local FILES = {
["cargo.lock"] = "application/json",
["flake.lock"] = "application/json",
license = "text/plain",
config ="text/plain",
config = "text/plain",
}
local EXTS = {
@@ -150,7 +150,7 @@ local EXTS = {
cod = "application/rim.cod",
com = "application/msdownload",
conf = "text/plain",
config ="text/plain",
config = "text/plain",
cpio = "application/cpio",
cpp = "text/c",
cpt = "application/mac-compactpro",
@@ -843,6 +843,7 @@ local EXTS = {
tfstate = "application/json",
tfvars = "text/hcl",
tga = "image/tga",
theme = "application/plain",
thmx = "application/ms-officetheme",
tif = "image/tiff",
tiff = "image/tiff",
@@ -1049,15 +1050,13 @@ local EXTS = {
zsh = "text/shellscript",
}
local options = ya.sync(
function(st)
local options = ya.sync(function(st)
return {
with_files = st.with_files,
with_exts = st.with_exts,
fallback_file1 = st.fallback_file1,
}
end
)
end)
local M = {}

View File

@@ -4,6 +4,7 @@ tex /home/liph/Documents/tex D,t
tank /mnt/tank t,t
scripts /home/liph/scripts d,s
r /home/liph/Documents/r D,r
programming /home/liph/programming p
podman /mnt/flash1/podman t,p
ohmyposh /home/liph/dotfiles/ohmyposh/.config/ohmyposh d,o
obsidian /home/liph/Documents/obsidian/vault D,o

View File

@@ -1 +0,0 @@
/home/liph/.config/yazi/themes/rose.toml

View File

@@ -1,187 +0,0 @@
# vim:fileencoding=utf-8:foldmethod=marker
# : Manager {{{
[mgr]
cwd = { fg = "#94e2d5" }
# TODO: remove
# Hovered
hovered = { reversed = true }
preview_hovered = { underline = true }
# Find
find_keyword = { fg = "#f9e2af", bold = true, italic = true, underline = true }
find_position = { fg = "#f5c2e7", bg = "reset", bold = true, italic = true }
# Marker
marker_copied = { fg = "#a6e3a1", bg = "#a6e3a1" }
marker_cut = { fg = "#f38ba8", bg = "#f38ba8" }
marker_marked = { fg = "#94e2d5", bg = "#94e2d5" }
marker_selected = { fg = "#f9e2af", bg = "#f9e2af" }
# Count
count_copied = { fg = "#1e1e2e", bg = "#a6e3a1" }
count_cut = { fg = "#1e1e2e", bg = "#f38ba8" }
count_selected = { fg = "#1e1e2e", bg = "#f9e2af" }
# Border
border_symbol = "│"
border_style = { fg = "#7f849c" }
# : }}}
# : Tabs {{{
[tabs]
active = { fg = "#1e1e2e", bg = "#89b4fa", bold = true }
inactive = { fg = "#89b4fa", bg = "#313244" }
# : }}}
# : Mode {{{
[mode]
normal_main = { fg = "#1e1e2e", bg = "#89b4fa", bold = true }
normal_alt = { fg = "#89b4fa", bg = "#313244" }
# Select mode
select_main = { fg = "#1e1e2e", bg = "#94e2d5", bold = true }
select_alt = { fg = "#94e2d5", bg = "#313244" }
# Unset mode
unset_main = { fg = "#1e1e2e", bg = "#f2cdcd", bold = true }
unset_alt = { fg = "#f2cdcd", bg = "#313244" }
# : }}}
# : Status bar {{{
[status]
# Permissions
perm_sep = { fg = "#7f849c" }
perm_type = { fg = "#89b4fa" }
perm_read = { fg = "#f9e2af" }
perm_write = { fg = "#f38ba8" }
perm_exec = { fg = "#a6e3a1" }
# Progress
progress_label = { fg = "#ffffff", bold = true }
progress_normal = { fg = "#a6e3a1", bg = "#45475a" }
progress_error = { fg = "#f9e2af", bg = "#f38ba8" }
# : }}}
# : Pick {{{
[pick]
border = { fg = "#89b4fa" }
active = { fg = "#f5c2e7", bold = true }
inactive = {}
# : }}}
# : Input {{{
[input]
border = { fg = "#89b4fa" }
title = {}
value = {}
selected = { reversed = true }
# : }}}
# : Completion {{{
[cmp]
border = { fg = "#89b4fa" }
# : }}}
# : Tasks {{{
[tasks]
border = { fg = "#89b4fa" }
title = {}
hovered = { fg = "#f5c2e7", bold = true }
# : }}}
# : Which {{{
[which]
mask = { bg = "#313244" }
cand = { fg = "#94e2d5" }
rest = { fg = "#9399b2" }
desc = { fg = "#f5c2e7" }
separator = "  "
separator_style = { fg = "#585b70" }
# : }}}
# : Help {{{
[help]
on = { fg = "#94e2d5" }
run = { fg = "#f5c2e7" }
hovered = { reversed = true, bold = true }
footer = { fg = "#313244", bg = "#cdd6f4" }
# : }}}
# : Spotter {{{
[spot]
border = { fg = "#89b4fa" }
title = { fg = "#89b4fa" }
tbl_col = { fg = "#94e2d5" }
tbl_cell = { fg = "#f5c2e7", bg = "#45475a" }
# : }}}
# : Notification {{{
[notify]
title_info = { fg = "#a6e3a1" }
title_warn = { fg = "#f9e2af" }
title_error = { fg = "#f38ba8" }
# : }}}
# : File-specific styles {{{
[filetype]
rules = [
# Image
{ mime = "image/*", fg = "#94e2d5" },
# Media
{ mime = "{audio,video}/*", fg = "#f9e2af" },
# Archive
{ mime = "application/{zip,rar,7z*,tar,gzip,xz,zstd,bzip*,lzma,compress,archive,cpio,arj,xar,ms-cab*}", fg = "#f5c2e7" },
# Document
{ mime = "application/{pdf,doc,rtf}", fg = "#a6e3a1" },
# Virtual file system
{ mime = "vfs/{absent,stale}", fg = "#9399b2" },
# Fallback
{ url = "*", fg = "#cdd6f4" },
{ url = "*/", fg = "#89b4fa" },
# TODO: remove
{ name = "*", fg = "#cdd6f4" },
{ name = "*/", fg = "#89b4fa" }
]
# : }}}

View File

@@ -1,318 +0,0 @@
# Theme Configuration
[theme]
# Theme metadata
name = "Dracula Pro"
author = "Enhanced Dracula Theme"
version = "2.0.0"
description = "A sophisticated dark theme based on Dracula color scheme"
# Color palette definition
[theme.colors]
background = "#282a36"
current_line = "#44475a"
foreground = "#f8f8f2"
comment = "#6272a4"
purple = "#bd93f9"
green = "#50fa7b"
orange = "#ffb86c"
red = "#ff5555"
pink = "#ff79c6"
cyan = "#8be9fd"
yellow = "#f1fa8c"
# Animation settings
[theme.animation]
frames_per_second = 60
duration = 0.2
easing = "easeInOutCubic"
# Manager
[manager]
# Enhanced preview options
preview = { tab_size = 2, max_width = 100, max_height = 50, cache_size = 50, scroll_offset = 5 }
preview_ratios = [1, 4, 4]
preview_shown = true
preview_service = { image = "ueberzug", video = "ffmpegthumbnailer", pdf = "pdftoppm" }
# Sophisticated hover effects
hovered = { fg = "#f8f8f2", bg = "#44475a", italic = true }
# Enhanced markers with animations
marker_copied = { fg = "#282a36", bg = "#50fa7b" }
# Dynamic loading indicators
loading_indicator_frames = "⣾⣽⣻⢿⡿⣟⣯⣷"
loading_style = { fg = "#bd93f9", bold = true }
# Enhanced folder icons
folder_icons = { default = " ", open = " ", empty = " ", empty_open = " ", symlink = " ", symlink_open = " ", error = " " }
file_size_units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]
# Status
[status]
# Dynamic status bar
refresh_rate = 1000
separator_open = ""
separator_close = ""
# Progress bar styling
progress_bar_style = { fg = "#bd93f9", bg = "#44475a" }
# Enhanced modes
mode_normal = { fg = "#282a36", bg = "#bd93f9", bold = true }
# Input
[input]
# Advanced input styling
cursor_style = { fg = "#f8f8f2", bg = "#6272a4", blink = true, blink_interval = 500 }
# History features
history_size = 100
history_unique = true
# Completion styling
completion_style = { selected_bg = "#44475a", selected_fg = "#f8f8f2", selected_bold = true, selected_italic = true }
# Notify
[notify]
# Enhanced notification system
position = "top-right"
timeout = 5000
max_width = 400
max_height = 200
margin = 10
padding = 8
[notify.levels]
info = { fg = "#50fa7b", bg = "#282a36", icon = " ", timeout = 3000 }
warn = { fg = "#f1fa8c", bg = "#282a36", icon = " ", timeout = 5000 }
error = { fg = "#ff5555", bg = "#282a36", icon = " ", timeout = 7000 }
debug = { fg = "#6272a4", bg = "#282a36", icon = "󰃤 ", timeout = 2000 }
[notify.animation]
enabled = true
duration = 200
slide_in = "right"
fade_out = true
[notify.border]
fg = "#bd93f9"
bg = "#282a36"
style = "rounded"
[notify.overlay]
bg = "#282a36"
blend = 0.8
# File-specific styles
[filetype]
rules = [
# Development Environment
{ name = ".env*", fg = "#50fa7b", bold = true, prefix = "󰙪 " },
{ name = ".git*", fg = "#ff5555", italic = true, prefix = " " },
{ name = ".docker*", fg = "#8be9fd", bold = true, prefix = "󰡨 " },
# Configuration Files
{ name = "*.{json,yaml,yml,toml,xml}", fg = "#8be9fd", italic = true, prefix = "󰘦 " },
{ name = "*.{ini,conf,cfg}", fg = "#6272a4", italic = true, prefix = " " },
# Web Development
{ name = "*.{html,htm}", fg = "#ff79c6", italic = true, prefix = "󰌝 " },
{ name = "*.{css,scss,sass,less}", fg = "#bd93f9", italic = true, prefix = "󰌜 " },
{ name = "*.{jsx,tsx}", fg = "#8be9fd", italic = true, prefix = "󰜈 " },
{ name = "*.{js,ts}", fg = "#f1fa8c", italic = true, prefix = "󰌞 " },
{ name = "*.vue", fg = "#50fa7b", italic = true, prefix = "󰡄 " },
{ name = "*.svelte", fg = "#ff5555", italic = true, prefix = "󰓓 " },
# Backend Development
{ name = "*.{py,pyc}", fg = "#50fa7b", italic = true, prefix = "󰌠 " },
{ name = "*.{rb,erb}", fg = "#ff5555", italic = true, prefix = "󰴭 " },
{ name = "*.{php,phar}", fg = "#bd93f9", italic = true, prefix = "󰌟 " },
{ name = "*.{java,jar}", fg = "#ff5555", italic = true, prefix = "󰬷 " },
{ name = "*.go", fg = "#8be9fd", italic = true, prefix = "󰟓 " },
{ name = "*.rs", fg = "#ff7043", italic = true, prefix = "󱘗 " },
# System Programming
{ name = "*.{c,h}", fg = "#bd93f9", italic = true, prefix = "󰙱 " },
{ name = "*.{cpp,hpp}", fg = "#ff79c6", italic = true, prefix = "󰙲 " },
{ name = "*.{asm,s}", fg = "#ff5555", italic = true, prefix = "󰋖 " },
# Build Systems
{ name = "*Makefile", fg = "#ff79c6", bold = true, prefix = "󰛕 " },
{ name = "*CMakeLists.txt", fg = "#ff79c6", bold = true, prefix = "󰔷 " },
{ name = "*.gradle", fg = "#8be9fd", bold = true, prefix = "󰏗 " },
# Package Managers
{ name = "package.json", fg = "#ff5555", bold = true, prefix = "󰎙 " },
{ name = "package-lock.json", fg = "#ff5555", italic = true, prefix = "󰎙 " },
{ name = "composer.json", fg = "#ff79c6", bold = true, prefix = "󰎙 " },
{ name = "Cargo.toml", fg = "#ff7043", bold = true, prefix = "󱘗 " },
# Documentation
{ name = "*.{md,mdx}", fg = "#f1fa8c", italic = true, prefix = "󰍔 " },
{ name = "*.rst", fg = "#f1fa8c", italic = true, prefix = "󰈙 " },
{ name = "*.pdf", fg = "#ff5555", bold = true, prefix = "󰈙 " },
{ name = "LICENSE*", fg = "#50fa7b", bold = true, prefix = "󰿃 " },
{ name = "README*", fg = "#50fa7b", bold = true, prefix = " " },
# Media with size categories
{ name = "*.{jpg,jpeg,png,gif}", size = "< 1MB", fg = "#8be9fd", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 10MB", fg = "#ffb86c", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "> 10MB", fg = "#ff5555", prefix = "󰋯 " },
# Video files with duration indicator
{ name = "*.{mp4,mkv}", duration = "< 10:00", fg = "#bd93f9", prefix = "󰕧 " },
{ name = "*.{mp4,mkv}", duration = "< 1:00:00", fg = "#ffb86c", prefix = "󰕧 " },
{ name = "*.{mp4,mkv}", duration = "> 1:00:00", fg = "#ff5555", prefix = "󰕧 " },
# Archives with compression ratio
{ name = "*.{zip,gz,tar}", ratio = "< 0.5", fg = "#50fa7b", prefix = "󰀼 " },
{ name = "*.{zip,gz,tar}", ratio = "< 0.7", fg = "#f1fa8c", prefix = "󰀼 " },
{ name = "*.{zip,gz,tar}", ratio = "> 0.7", fg = "#ff5555", prefix = "󰀼 " },
# Special Directories
{ name = "node_modules", fg = "#6272a4", prefix = "󰎙 " },
{ name = ".git", fg = "#ff5555", prefix = " " },
{ name = ".github", fg = "#bd93f9", prefix = " " },
{ name = "dist", fg = "#6272a4", prefix = "󰏗 " },
{ name = "build", fg = "#6272a4", prefix = "󱁤 " },
# Additional file types
# Audio files
{ name = "*.{mp3,flac,m4a,wav,ogg}", fg = "#bd93f9", italic = true, prefix = "󰎈 " },
# Font files
{ name = "*.{ttf,otf,woff,woff2}", fg = "#ff79c6", italic = true, prefix = "󰛖 " },
# Database files
{ name = "*.{sql,sqlite,db}", fg = "#8be9fd", italic = true, prefix = "󰆼 " },
# Shell scripts
{ name = "*.{sh,bash,zsh,fish}", fg = "#50fa7b", italic = true, prefix = "󰆍 " },
# Virtual environments
{ name = "venv", fg = "#6272a4", prefix = "󰆧 " },
{ name = ".env", fg = "#50fa7b", prefix = "󰆧 " },
# Container files
{ name = "*.dockerfile", fg = "#8be9fd", bold = true, prefix = "󰡨 " },
{ name = "docker-compose*.{yml,yaml}", fg = "#8be9fd", bold = true, prefix = "󰡨 " },
# Security files
{ name = "*.{pem,crt,ca,key}", fg = "#ff5555", bold = true, prefix = "󰌋 " },
# Improved size-based rules for media
{ name = "*.{jpg,jpeg,png,gif}", size = "< 100KB", fg = "#8be9fd", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 1MB", fg = "#bd93f9", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 10MB", fg = "#ffb86c", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "> 10MB", fg = "#ff5555", prefix = "󰋯 " },
# Default Fallbacks
{ name = "*", fg = "#f8f8f2" },
{ name = "*/", fg = "#bd93f9", bold = true, prefix = " " },
# Additional Development Files
{ name = "*.{proto}", fg = "#bd93f9", italic = true, prefix = "󰯍 " },
{ name = "*.{graphql,gql}", fg = "#ff79c6", italic = true, prefix = "󰡷 " },
{ name = "*.{tf,tfvars}", fg = "#8be9fd", italic = true, prefix = "󱁢 " },
# Container and Cloud
{ name = "*.{yaml,yml}", pattern = "^k8s|^kubernetes", fg = "#8be9fd", bold = true, prefix = "󱃾 " },
{ name = "*.{yaml,yml}", pattern = "^helm", fg = "#8be9fd", bold = true, prefix = "󰠿 " },
# Data Files
{ name = "*.{csv,tsv}", fg = "#50fa7b", italic = true, prefix = "󰈛 " },
{ name = "*.{parquet,avro}", fg = "#bd93f9", italic = true, prefix = "󰋖 " },
# Size-based rules for documents
{ name = "*.{pdf,epub,mobi}", size = "< 1MB", fg = "#8be9fd", prefix = "󰈙 " },
{ name = "*.{pdf,epub,mobi}", size = "< 10MB", fg = "#ffb86c", prefix = "󰈙 " },
{ name = "*.{pdf,epub,mobi}", size = "> 10MB", fg = "#ff5555", prefix = "󰈙 " },
# Additional Development Files
{ name = "*.{sol}", fg = "#bd93f9", italic = true, prefix = "󰡪 " }, # Solidity files
{ name = "*.{ex,exs}", fg = "#bd93f9", italic = true, prefix = "󰡁 " }, # Elixir files
{ name = "*.{kt,kts}", fg = "#ff79c6", italic = true, prefix = "󱈙 " }, # Kotlin files
{ name = "*.{swift}", fg = "#ff5555", italic = true, prefix = "󰛥 " }, # Swift files
# Config Files
{ name = "*.{nginx,nginx.conf}", fg = "#50fa7b", italic = true, prefix = "󰻭 " },
{ name = "*{webpack}*", fg = "#8be9fd", bold = true, prefix = "󰜫 " },
# ML/Data Science
{ name = "*.{ipynb}", fg = "#ff79c6", italic = true, prefix = "󰠏 " },
{ name = "*.{pkl,pickle}", fg = "#50fa7b", italic = true, prefix = "󰜎 " },
# 3D/Graphics
{ name = "*.{blend}", fg = "#ff79c6", italic = true, prefix = "󰂫 " },
{ name = "*.{fbx,obj,stl}", fg = "#8be9fd", italic = true, prefix = "󰆧 " },
# More granular size-based rules for media files
{ name = "*.{jpg,jpeg,png,gif}", size = "< 50KB", fg = "#8be9fd", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 500KB", fg = "#bd93f9", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 2MB", fg = "#ffb86c", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "< 20MB", fg = "#ff7043", prefix = "󰋯 " },
{ name = "*.{jpg,jpeg,png,gif}", size = "> 20MB", fg = "#ff5555", prefix = "󰋯 " },
# Size categories for video files
{ name = "*.{mp4,mkv,avi,mov}", size = "< 100MB", fg = "#8be9fd", prefix = "󰕧 " },
{ name = "*.{mp4,mkv,avi,mov}", size = "< 1GB", fg = "#ffb86c", prefix = "󰕧 " },
{ name = "*.{mp4,mkv,avi,mov}", size = "> 1GB", fg = "#ff5555", prefix = "󰕧 " },
]
# Keybindings
[keys]
# Visual key hints
show_hints = true
hint_style = { fg = "#6272a4", bg = "#44475a", italic = true }
# Command palette
command_palette = { bg = "#282a36", fg = "#f8f8f2", selected_bg = "#44475a", selected_fg = "#f8f8f2", border = "#bd93f9" }
# Preview
[preview]
tab_size = 2
max_width = 120
max_height = 60
cache_dir = "/tmp/yazi"
[preview.image]
enabled = true
format = "rgb"
max_width = 1920
max_height = 1080
quality = 90
animate = true
cache = true
[preview.preview_service]
image = "ueberzug"
video = "ffmpegthumbnailer"
pdf = "pdftoppm"
epub = "epub-thumbnailer"
office = "libreoffice --headless --convert-to pdf"
markdown = "glow"
[preview.syntax]
theme = "Dracula"
background = "#282a36"
# Opener
[opener]
edit = [
{ exec = 'nvim "$@"', desc = "Edit with Neovim" },
{ exec = 'code "$@"', desc = "Edit with VS Code" }
]
open = [
{ exec = 'xdg-open "$@"', desc = "Open with system default" },
{ exec = 'firefox "$@"', desc = "Open in Firefox" }
]
reveal = [
{ exec = 'nautilus "$@"', desc = "Reveal in file manager" }
]
# Enhanced preview features
max_preview_size = 10485760 # 10MB limit for preview
scroll_offset = 5
scroll_smooth = true

View File

@@ -1,72 +0,0 @@
# Gruvbox Material Dark theme for Yazi
# Based on https://github.com/sainnhe/gruvbox-material
[manager]
cwd = { fg = "#d8a657" } # yellow
# Hovered
hovered = { fg = "#282828", bg = "#a9b665" } # green background
preview_hovered = { underline = true }
# Find
find_keyword = { fg = "#282828", bg = "#d8a657" } # yellow
find_position = { fg = "#282828", bg = "#7daea3" } # aqua
# Marker
marker_copied = { fg = "#a9b665", bg = "#a9b665" } # green
marker_cut = { fg = "#ea6962", bg = "#ea6962" } # red
marker_marked = { fg = "#7daea3", bg = "#7daea3" } # aqua
marker_selected = { fg = "#d8a657", bg = "#d8a657" } # yellow
# Tab
tab_active = { fg = "#282828", bg = "#a9b665" } # green
tab_inactive = { fg = "#d4be98", bg = "#3c3836" } # fg on bg1
tab_width = 1
# Count
count_copied = { fg = "#282828", bg = "#a9b665" } # green
count_cut = { fg = "#282828", bg = "#ea6962" } # red
count_selected = { fg = "#282828", bg = "#7daea3" } # aqua
# Border
border_symbol = "│"
border_style = { fg = "#504945" } # bg2
# Syntect theme for preview
syntect_theme = "~/.config/yazi/Gruvbox-Material-Dark.tmTheme"
[status]
separator_open = ""
separator_close = ""
separator_style = { fg = "#504945", bg = "#504945" } # bg2
# Mode
mode_normal = { fg = "#282828", bg = "#7daea3", bold = true } # aqua
mode_select = { fg = "#282828", bg = "#d8a657", bold = true } # yellow
mode_unset = { fg = "#282828", bg = "#ea6962", bold = true } # red
# Progress
progress_label = { fg = "#d4be98", bold = true } # fg
progress_normal = { fg = "#7daea3", bg = "#3c3836" } # aqua on bg1
progress_error = { fg = "#ea6962", bg = "#3c3836" } # red on bg1
# Permissions
permissions_t = { fg = "#a9b665" } # green
permissions_r = { fg = "#d8a657" } # yellow
permissions_w = { fg = "#ea6962" } # red
permissions_x = { fg = "#7daea3" } # aqua
permissions_s = { fg = "#504945" } # bg2
[input]
border = { fg = "#7daea3" } # aqua
title = { fg = "#d4be98" } # fg
value = { fg = "#d4be98" } # fg
selected = { reversed = true }
[select]
border = { fg = "#7daea3" } # aqua
active = { fg = "#d8a657" } # yellow
inactive = { fg = "#7c6f64" } # bg3
[completion]
border = { fg = "#7daea3" } # aqua

View File

@@ -1,4 +0,0 @@
[flavor]
dark = "rose-pine-moon"
# or "rose-pine-moon"
# or light = "rose-pine-dawn"