Compare commits

..

30 Commits

Author SHA1 Message Date
liph
a5be19430e updated podman-update.sh 2026-02-25 10:58:58 +01:00
liph
d5d9075ad2 added sudo to alias for podman-update.sh 2026-02-25 10:50:25 +01:00
liph
845ef5d076 updated podup scritp 2026-02-25 10:35:13 +01:00
liph
07d8051731 updated scripts (added podman-update.sh) 2026-02-25 10:26:28 +01:00
liph
1baf5ecddf updatged nvim-surround 2026-02-23 19:01:51 +01:00
liph
65658a34b8 updated alias 2026-02-23 17:43:24 +01:00
liph
f901141e5f updated tmux config and plugins, added tmuxp to dotfiles 2026-02-23 17:37:56 +01:00
liph
8925d9677e added ansible script 2026-02-16 23:40:30 +01:00
liph
e6e444fff7 updated yazi conf. nvim completions and latex 2026-02-13 09:37:55 +01:00
liph
84b025eb0e add username to ohmyposh config 2026-02-06 15:06:49 +01:00
liph
e32afefde9 fixed the obsidian errors 2026-02-06 14:27:53 +01:00
liph
f5ac9bce3e deleted the cmp-tabnine 2026-02-06 14:24:34 +01:00
liph
ab69cc08f2 added funtioning mason.lua back 2026-02-06 10:44:38 +01:00
liph
7b590ef2ec deleted blink.lua but completed the completion.lua 2026-02-06 10:08:00 +01:00
liph
b2194f79dd added hostname var in ohmyposh 2026-02-05 17:07:01 +01:00
liph
5a78d47050 fixed blink.lua 2026-02-05 16:37:16 +01:00
liph
001c74dfa7 fixed treesitter 2026-02-05 15:55:31 +01:00
liph
8b28075f40 fixed nvim setup? 2026-02-05 15:31:17 +01:00
liph
2e1ccf0cc2 added new configs with the dell comp 2026-02-05 13:46:21 +01:00
liph22
f3d1e7fdb1 added themes, niri waybar updat, nvim plugins updates and additions 2026-02-03 09:43:40 +01:00
liph22
1b652298ef updatet colorschemes 2026-01-19 21:05:12 +01:00
liph22
0ffe6606b6 changes in aerc, updated nvim plugins, yazi config, niri, changed hypr dotfiles folders 2026-01-15 09:33:02 +01:00
liph22
b9fe2697a0 added toggle-preview plugin in yazi, status bar with omp 2026-01-13 15:39:15 +01:00
liph22
5fc9e27e67 updated dotfiles 2026-01-13 09:28:51 +01:00
liph22
c4df37dacb added niri config with waybar, rip, nvim update, brightness sh 2026-01-12 22:26:10 +01:00
liph22
d7b808ce59 adde latex lsp and language 2026-01-10 14:22:02 +01:00
liph22
b7cfdb00a3 updated nvim 2026-01-08 22:04:23 +01:00
liph22
ce7e3a9824 updated yazi config, hyprland, tmux 2026-01-08 12:36:24 +01:00
liph22
70c6316f8e changes in whoosh 2026-01-07 15:00:47 +01:00
liph22
cdc1aa61c4 changes in whoosh 2026-01-07 15:00:35 +01:00
305 changed files with 33775 additions and 2922 deletions

View File

@@ -9,40 +9,43 @@ 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
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
outgoing = smtp://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:587
default = INBOX
from = Liph <spam@liphlink.xyz>
copy-to = Sent
archive = Archive
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
source = imaps://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:993
outgoing = smtp://spam%40liphlink.xyz:ANZ6JJPBiB7k1c7k@mail.liphlink.xyz:587
default = INBOX
from = Liph <spam@liphlink.xyz>
default = Inbox
copy-to = Sent
archive = Archive
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
[proton]
source = imap+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1144
outgoing = smtp+insecure://liiph%40protonmail.com:dRvBWYW3uERY6xqXDgJLeQ@127.0.0.1:1026
source = imap+insecure://liiph%40protonmail.com:YF37rs63oirevVIs-mq0tg@127.0.0.1:1143
outgoing = smtp+insecure://liiph%40protonmail.com:YF37rs63oirevVIs-mq0tg@127.0.0.1:1025
default = INBOX
from = Liph <spam@liphlink.xyz>
from = Liph <liiph@proton.me>
copy-to = Sent
archive = Archive
postpone = Drafts

View File

@@ -96,9 +96,10 @@ index-format=notmuch://~/.local/share/mail
#
# Default: flags:4,name<20%,subject,date>=
#index-columns=flags:4,name<20%,subject,date>=
# border-char-vertical="┃"
border-char-vertical="│"
border-char-horizontal="─"
styleset-name=catppuccin-mocha
styleset-name="gruvbox-dark"
#
# Each name in index-columns must have a corresponding column-$name setting.
@@ -576,11 +577,11 @@ sort = -r date
[viewer]
# pager=less -Rc
pager=bat
html-unsafe-images=false
html-filter=dante
pager= bat --style=plain --paging=always
# html-unsafe-images=false
# html-filter=dante
image/*=chafa -f kitty -s ${width}x${height}
text/html=dante
text/html= ~/.local/bin/mailhtml2md
application/pdf=zathura
video/*=mpv
header-layout=From,To,Subject,Date
@@ -637,9 +638,16 @@ header-layout=From,To,Subject,Date
#parse-http-links=true
[compose]
editor=nvim # or your preferred editor
editor=nvim +'set ft=mail'
# editor=nvim +setf\ mail
# editor=nvim # or your preferred editor
# Address book command
address-book-cmd=abook --mutt-query '%s'
# Specifies the command to run the editor with. It will be shown in an embedded
# Auto-complete addresses
# address-book-cmd-match='%s'
# address-book-cmd=abook --mutt-query '%s'
# terminal, though it may also launch a graphical window if the environment
# supports it. Defaults to $EDITOR, or vi.
#editor=

View File

@@ -16,10 +16,10 @@
[messages]
# Switch between Adresses
,1 = :change-tab phil_live<Enter>
,2 = :change-tab phil_notmuch<Enter>
,3 = :change-tab spam_live<Enter>
,4 = :change-tab spam_notmuch<Enter>
,5 = :change-tab proton<Enter>
# ,2 = :change-tab phil_notmuch<Enter>
,2 = :change-tab spam_live<Enter>
# ,4 = :change-tab spam_notmuch<Enter>
,3 = :change-tab proton<Enter>
# d = :modify -inbox +deleted<Enter>
# a = :modify -inbox<Enter>

View File

@@ -1,51 +0,0 @@
*.default=true
*.normal=true
default.fg=#cdd6f4
error.fg=#f38ba8
warning.fg=#fab387
success.fg=#a6e3a1
tab.fg=#6c7086
tab.bg=#181825
tab.selected.fg=#cdd6f4
tab.selected.bg=#1e1e2e
tab.selected.bold=true
border.fg=#11111b
border.bold=true
msglist_unread.bold=true
msglist_flagged.fg=#f9e2af
msglist_flagged.bold=true
msglist_result.fg=#89b4fa
msglist_result.bold=true
msglist_*.selected.bold=true
msglist_*.selected.bg=#313244
dirlist_*.selected.bold=true
dirlist_*.selected.bg=#313244
statusline_default.fg=#9399b2
statusline_default.bg=#313244
statusline_error.bold=true
statusline_success.bold=true
selector_focused.bg=#313244
completion_default.selected.bg=#313244
[viewer]
url.fg=#89b4fa
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#89b4fa
diff_chunk_func.fg=#89b4fa
diff_chunk_func.bold=true
diff_add.fg=#a6e3a1
diff_del.fg=#f38ba8
quote_*.fg=#6c7086
quote_1.fg=#9399b2

View File

@@ -0,0 +1,53 @@
*.default=true
*.normal=true
*.selected.bold=true
*.selected.bg=#ebdbb2
*.selected.fg=#282828
default.fg=#89b482
error.fg=#cc241d
warning.fg=#d65d0e
success.fg=#D8A657
tab.fg=#7c6f64
tab.bg=#282823
tab.selected.fg=#d5c4a1
tab.selected.bg=#282828
tab.selected.bold=true
border.fg=#d5c4a1
border.bold=true
msglist_unread.bold=true
msglist_unread.fg=#d8a657
msglist_unread.selected.bg=#ebdbb2
msglist_unread.selected.fg=#282828
msglist_flagged.fg=#f9e2af
msglist_flagged.bold=true
msglist_result.fg=#458588
msglist_result.bold=true
msglist_*.selected.bold=true
msglist_*.selected.bg=#ebdbb2
dirlist_*.selected.bold=true
dirlist_*.selected.bg=#ebdbb2
statusline_default.fg=#ebdbb2
statusline_default.bg=#282828
statusline_error.bold=true
statusline_success.bold=true
[viewer]
url.fg=#458588
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#458588
diff_chunk_func.fg=#458588
diff_chunk_func.bold=true
diff_add.fg=#D8A657
diff_del.fg=#cc241d
quote_*.fg=#d5c4a1
quote_1.fg=#b16286

View File

@@ -0,0 +1,85 @@
# vim: filetype=dosini
# Rosé Pine Theme for Aerc
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
# Save as ~/.config/aerc/stylesets/rose-pine
# Base styling
default.fg=#9ccfd8
default.bg=#191724
success.fg=#31748f
error.fg=#eb6f92
warning.fg=#f6c177
title.bg=#26233a
title.fg=#c4a7e7
header.fg=#c4a7e7
spinner.fg=#6e6a86
part_filename.fg=#f6c177
part_mimetype.fg=#c4a7e7
# Global selection
*.selected.fg=#191724
*.selected.bg=#ebbcba
*.selected.bold=true
# Message list
msglist_default.fg=#9ccfd8
msglist_default.bg=#1f1d2e
msglist_unread.fg=#524f67
msglist_unread.bold=true
msglist_read.fg=#6e6a86
msglist_flagged.fg=#f6c177
msglist_flagged.bold=true
msglist_deleted.bg=#eb6f92
msglist_deleted.fg=#191724
msglist_deleted.bold=true
msglist_marked.fg=#191724
msglist_marked.bg=#31748f
msglist_marked.bold=true
# Status bar
statusline_*.bold=true
statusline_*.fg=#191724
statusline_default.fg=#9ccfd8
statusline_default.bg=#191724
statusline_error.bg=#eb6f92
statusline_success.bg=#31748f
# Completion
completion_default.fg=#9ccfd8
completion_default.bg=#1f1d2e
completion_gutter.bg=#26233a
completion_description.bg=#26233a
# Scrollbars
*_pill.fg=#191724
*_pill.bg=#6e6a86
# Border
border.fg=#191724
border.bg=#191724
# Tab
tab.bg=#191724
tab.selected.fg=#ebbcba
tab.selected.bg=#191724
tab.selected.bold=true
[viewer]
url.fg=#c4a7e7
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#c4a7e7
diff_chunk_func.fg=#c4a7e7
diff_chunk_func.bold=true
diff_add.fg=#524f67
diff_del.fg=#eb6f92
quote_*.fg=#908caa
quote_1.fg=#21202e

View File

@@ -0,0 +1,85 @@
# vim: filetype=dosini
# Rosé Pine Theme for Aerc
# All natural pine, faux fur and a bit of soho vibes for the classy minimalist
# Save as ~/.config/aerc/stylesets/rose-pine
# Base styling
default.fg=#9ccfd8
default.bg=#191724
success.fg=#31748f
error.fg=#eb6f92
warning.fg=#f6c177
title.bg=#26233a
title.fg=#c4a7e7
header.fg=#c4a7e7
spinner.fg=#6e6a86
part_filename.fg=#f6c177
part_mimetype.fg=#c4a7e7
# Global selection
*.selected.fg=#191724
*.selected.bg=#ebbcba
*.selected.bold=true
# Message list
msglist_default.fg=#9ccfd8
msglist_default.bg=#1f1d2e
msglist_unread.fg=#524f67
msglist_unread.bold=true
msglist_read.fg=#6e6a86
msglist_flagged.fg=#f6c177
msglist_flagged.bold=true
msglist_deleted.bg=#eb6f92
msglist_deleted.fg=#191724
msglist_deleted.bold=true
msglist_marked.fg=#191724
msglist_marked.bg=#31748f
msglist_marked.bold=true
# Status bar
statusline_*.bold=true
statusline_*.fg=#191724
statusline_default.fg=#9ccfd8
statusline_default.bg=#191724
statusline_error.bg=#eb6f92
statusline_success.bg=#31748f
# Completion
completion_default.fg=#9ccfd8
completion_default.bg=#1f1d2e
completion_gutter.bg=#26233a
completion_description.bg=#26233a
# Scrollbars
*_pill.fg=#191724
*_pill.bg=#6e6a86
# Border
border.fg=#191724
border.bg=#191724
# Tab
tab.bg=#191724
tab.selected.fg=#ebbcba
tab.selected.bg=#191724
tab.selected.bold=true
[viewer]
url.fg=#c4a7e7
url.underline=true
header.bold=true
signature.dim=true
diff_meta.bold=true
diff_chunk.fg=#c4a7e7
diff_chunk_func.fg=#c4a7e7
diff_chunk_func.bold=true
diff_add.fg=#524f67
diff_del.fg=#eb6f92
quote_*.fg=#908caa
quote_1.fg=#21202e

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! 🎉"

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View File

Before

Width:  |  Height:  |  Size: 778 KiB

After

Width:  |  Height:  |  Size: 778 KiB

View File

Before

Width:  |  Height:  |  Size: 974 KiB

After

Width:  |  Height:  |  Size: 974 KiB

View File

Before

Width:  |  Height:  |  Size: 430 KiB

After

Width:  |  Height:  |  Size: 430 KiB

View File

@@ -1,8 +1,8 @@
source = $HOME/.config/hypr/mocha.conf
source = /home/liph/.config/hypr/mocha.conf
$accent = $mauve
$accentAlpha = $mauveAlpha
$font = JetBrainsMono Nerd Font
$font = Inconsolata Nerd Font
# GENERAL
general {
@@ -13,7 +13,7 @@ general {
# BACKGROUND
background {
monitor =
path = $WALLPAPER
path = /home/liph/.config/backgrounds/universe/magenta.jpg
blur_passes = 2
color = $base
}

View File

@@ -2,3 +2,4 @@ wallpaper {
monitor =
path = $WALLPAPER
}
splash = false

View File

@@ -0,0 +1,16 @@
#version 300 es
precision mediump float;
in vec2 v_texcoord;
out vec4 fragColor;
uniform sampler2D tex;
void main() {
vec4 pixColor = texture(tex, v_texcoord);
// Adjust brightness
pixColor.rgb *= 1.00;
fragColor = pixColor;
}

View File

@@ -0,0 +1 @@
/home/liph/.config/systemd/user/gammastep.service

View File

@@ -0,0 +1 @@
/home/liph/.config/systemd/user/kanata.service

View File

@@ -0,0 +1,13 @@
[Unit]
Description=Gamma-colour filter (red-shift) for Wayland
After=graphical-session.target
PartOf=graphical-session.target
[Service]
Type=simple
ExecStart=/usr/bin/gammastep -l 47.38:8.54 -t 6000:4000
Restart=on-failure
RestartSec=3
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,83 @@
[38:2:144:140:170;48:2:42:39:63m [38:2:109:128:134m [22;1;38:2:144:140:170mwob.service
 1 [38:2:156:207:216m┆
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[38:2:110:106:134m~
[22;1;38:2:35:33:54;48:2:234:154:151m NORMAL [22;38:2:234:154:151;48:2:42:39:63m[38:2:224:222:244m wob.service [38:2:57:53:82m[38:2:234:154:151;48:2:57:53:82m Top [22;1;38:2:35:33:54;48:2:234:154:151m 1:1

View File

@@ -10,13 +10,13 @@ Environment=DISPLAY=:0
# WARNING: doing so will require the service to run as an elevated user such as root.
# Implementing least privilege access is an exercise left to the reader.
#
CPUSchedulingPolicy=rr
CPUSchedulingPriority=99
IOSchedulingClass=realtime
#CPUSchedulingPolicy=rr
#CPUSchedulingClass=realtim
Nice=-20
Type=simple
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd"
Restart=no
ExecStart=/usr/bin/sh -c "exec $$(which kanata) --cfg $$HOME/.config/kanata/config.kbd --no-wait"
Restart=on failure
RestartkSec=3
[Install]
WantedBy=default.target

View File

@@ -1,80 +1,47 @@
# vim:ft=kitty
background #282828
foreground #d4be98
## name: Catppuccin-Mocha
## author: Pocco81 (https://github.com/Pocco81)
## license: MIT
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
## blurb: Soothing pastel theme for the high-spirited!
selection_background #d4be98
selection_foreground #282828
window_padding_width 5 10
cursor #a89984
cursor_text_color background
# The basic colors
foreground #CDD6F4
background #1E1E2E
selection_foreground #1E1E2E
selection_background #F5E0DC
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
# Cursor colors
cursor #F5E0DC
cursor_text_color #1E1E2E
# Black
color0 #665c54
color8 #928374
# URL underline color when hovering with mouse
url_color #F5E0DC
# Red
color1 #ea6962
color9 #ea6962
# Kitty window border colors
active_border_color #B4BEFE
inactive_border_color #6C7086
bell_border_color #F9E2AF
# Green
color2 #a9b665
color10 #a9b665
# OS Window titlebar colors
wayland_titlebar_color system
macos_titlebar_color system
# Yellow
color3 #e78a4e
color11 #d8a657
# Tab bar colors
active_tab_foreground #11111B
active_tab_background #CBA6F7
inactive_tab_foreground #CDD6F4
inactive_tab_background #181825
tab_bar_background #11111B
# Blue
color4 #7daea3
color12 #7daea3
# Colors for marks (marked text in the terminal)
mark1_foreground #1E1E2E
mark1_background #B4BEFE
mark2_foreground #1E1E2E
mark2_background #CBA6F7
mark3_foreground #1E1E2E
mark3_background #74C7EC
# Magenta
color5 #d3869b
color13 #d3869b
# The 16 terminal colors
# Cyan
color6 #89b482
color14 #89b482
# black
color0 #45475A
color8 #585B70
# red
color1 #F38BA8
color9 #F38BA8
# green
color2 #A6E3A1
color10 #A6E3A1
# yellow
color3 #F9E2AF
color11 #F9E2AF
# blue
color4 #89B4FA
color12 #89B4FA
# magenta
color5 #F5C2E7
color13 #F5C2E7
# cyan
color6 #94E2D5
color14 #94E2D5
# white
color7 #BAC2DE
color15 #A6ADC8
# White
color7 #d4be98
color15 #d4be98

View File

@@ -1,14 +1,23 @@
# BEGIN_KITTY_THEME
# Catppuccin-Mocha
# Gruvbox Material
include current-theme.conf
# END_KITTY_THEME
#
shell zsh --login
font_family Inconsolata Nerd Font
font_family JetBrainsMono Nerd Font
bold_font auto
italic_font auto
bold_italic_font auto
font_size 14
font_size 12
background_opacity 0.90
term xterm-256color
allow_hyperlinks yes
copy_on_select yes
paste_actions confirm-if-large
background_opacity 0.9

View File

@@ -0,0 +1,23 @@
# BEGIN_KITTY_THEME
# Kanagawa
include current-theme.conf
# END_KITTY_THEME
#
shell zsh --login
font_family JetBrainsMono Nerd Font
bold_font auto
italic_font auto
bold_italic_font auto
font_size 12
background_opacity 0.90
term xterm-256color
allow_hyperlinks yes
copy_on_select yes
paste_actions confirm-if-large

View File

@@ -0,0 +1,104 @@
# vim:ft=kitty
## name: Carbonfox
## author: EdenEast
## license: MIT
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/carbonfox/kitty.conf
## blurb: Carbonfox theme from the neovim colorscheme nightfox.nvim.
#: All the settings below are colors, which you can choose to modify, or use the
#: defaults. You can also add non-color based settings if needed but note that
#: these will not work with using kitty @ set-colors with this theme. For a
#: reference on what these settings do see https://sw.kovidgoyal.net/kitty/conf/
#: The basic colors
foreground #f2f4f8
background #161616
selection_foreground #f2f4f8
selection_background #2a2a2a
#: Cursor colors
cursor #f2f4f8
cursor_text_color #161616
#: URL underline color when hovering with mouse
url_color #25be6a
#: kitty window border colors and terminal bell colors
active_border_color #78a9ff
inactive_border_color #535353
bell_border_color #3ddbd9
# visual_bell_color none
#: OS Window titlebar colors
# wayland_titlebar_color system
# macos_titlebar_color system
#: Tab bar colors
active_tab_foreground #0c0c0c
active_tab_background #78a9ff
inactive_tab_foreground #6e6f70
inactive_tab_background #2a2a2a
# tab_bar_background none
# tab_bar_margin_color none
#: Colors for marks (marked text in the terminal)
# mark1_foreground black
# mark1_background #98d3cb
# mark2_foreground black
# mark2_background #f2dcd3
# mark3_foreground black
# mark3_background #f274bc
#: The basic 16 colors
#: black
color0 #282828
color8 #484848
#: red
color1 #ee5396
color9 #f16da6
#: green
color2 #25be6a
color10 #46c880
#: yellow
color3 #ebcb8b
color11 #f0d399
#: blue
color4 #78a9ff
color12 #8cb6ff
#: magenta
color5 #be95ff
color13 #c8a5ff
#: cyan
color6 #33b1ff
color14 #52bdff
#: white
color7 #dfdfe0
color15 #e4e4e5
#: You can set the remaining 240 colors as color16 to color255.
color16 #3ddbd9
color17 #ff7eb6

View File

@@ -0,0 +1,80 @@
# vim:ft=kitty
## name: Catppuccin-Mocha
## author: Pocco81 (https://github.com/Pocco81)
## license: MIT
## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf
## blurb: Soothing pastel theme for the high-spirited!
# The basic colors
foreground #CDD6F4
background #1E1E2E
selection_foreground #1E1E2E
selection_background #F5E0DC
# Cursor colors
cursor #F5E0DC
cursor_text_color #1E1E2E
# URL underline color when hovering with mouse
url_color #F5E0DC
# Kitty window border colors
active_border_color #B4BEFE
inactive_border_color #6C7086
bell_border_color #F9E2AF
# OS Window titlebar colors
wayland_titlebar_color system
macos_titlebar_color system
# Tab bar colors
active_tab_foreground #11111B
active_tab_background #CBA6F7
inactive_tab_foreground #CDD6F4
inactive_tab_background #181825
tab_bar_background #11111B
# Colors for marks (marked text in the terminal)
mark1_foreground #1E1E2E
mark1_background #B4BEFE
mark2_foreground #1E1E2E
mark2_background #CBA6F7
mark3_foreground #1E1E2E
mark3_background #74C7EC
# The 16 terminal colors
# black
color0 #45475A
color8 #585B70
# red
color1 #F38BA8
color9 #F38BA8
# green
color2 #A6E3A1
color10 #A6E3A1
# yellow
color3 #F9E2AF
color11 #F9E2AF
# blue
color4 #89B4FA
color12 #89B4FA
# magenta
color5 #F5C2E7
color13 #F5C2E7
# cyan
color6 #94E2D5
color14 #94E2D5
# white
color7 #BAC2DE
color15 #A6ADC8

View File

@@ -0,0 +1,66 @@
# https://draculatheme.com/kitty
#
# Installation instructions:
#
# cp dracula.conf ~/.config/kitty/
# echo "include dracula.conf" >> ~/.config/kitty/kitty.conf
#
# Then reload kitty for the config to take affect.
# Alternatively copy paste below directly into kitty.conf
foreground #f8f8f2
background #282a36
selection_foreground #ffffff
selection_background #44475a
url_color #8be9fd
# black
color0 #21222c
color8 #6272a4
# red
color1 #ff5555
color9 #ff6e6e
# green
color2 #50fa7b
color10 #69ff94
# yellow
color3 #f1fa8c
color11 #ffffa5
# blue
color4 #bd93f9
color12 #d6acff
# magenta
color5 #ff79c6
color13 #ff92df
# cyan
color6 #8be9fd
color14 #a4ffff
# white
color7 #f8f8f2
color15 #ffffff
# Cursor colors
cursor #f8f8f2
cursor_text_color background
# Tab bar colors
active_tab_foreground #282a36
active_tab_background #f8f8f2
inactive_tab_foreground #282a36
inactive_tab_background #6272a4
# Marks
mark1_foreground #282a36
mark1_background #ff5555
# Splits/Windows
active_border_color #f8f8f2
inactive_border_color #6272a4

View File

@@ -0,0 +1,22 @@
# Color theme: Everforest Dark Medium
# Auto-generated by Gogh (https://Gogh-Co.github.io/Gogh/)
color0 #343F44
color1 #E67E80
color2 #A7C080
color3 #DBBC7F
color4 #7FBBB3
color5 #D699B6
color6 #83C092
color7 #D3C6AA
color8 #5C6A72
color9 #F85552
color10 #8DA101
color11 #DFA000
color12 #3A94C5
color13 #DF69BA
color14 #35A77C
color15 #DFDDC8
background #2D353B
foreground #D3C6AA
cursor #D3C6AA

View File

@@ -0,0 +1,47 @@
background #282828
foreground #d4be98
selection_background #d4be98
selection_foreground #282828
cursor #a89984
cursor_text_color background
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
# Black
color0 #665c54
color8 #928374
# Red
color1 #ea6962
color9 #ea6962
# Green
color2 #a9b665
color10 #a9b665
# Yellow
color3 #e78a4e
color11 #d8a657
# Blue
color4 #7daea3
color12 #7daea3
# Magenta
color5 #d3869b
color13 #d3869b
# Cyan
color6 #89b482
color14 #89b482
# White
color7 #d4be98
color15 #d4be98

View File

@@ -0,0 +1,47 @@
# vim:ft=kitty
## name: Kanagawa
## license: MIT
## author: Tommaso Laurenzi
## upstream: https://github.com/rebelot/kanagawa.nvim/
background #1F1F28
foreground #DCD7BA
selection_background #2D4F67
selection_foreground #C8C093
url_color #72A7BC
cursor #C8C093
# Tabs
active_tab_background #1F1F28
active_tab_foreground #C8C093
inactive_tab_background #1F1F28
inactive_tab_foreground #727169
#tab_bar_background #15161E
# normal
color0 #16161D
color1 #C34043
color2 #76946A
color3 #C0A36E
color4 #7E9CD8
color5 #957FB8
color6 #6A9589
color7 #C8C093
# bright
color8 #727169
color9 #E82424
color10 #98BB6C
color11 #E6C384
color12 #7FB4CA
color13 #938AA9
color14 #7AA89F
color15 #DCD7BA
# extended colors
color16 #FFA066
color17 #FF5D62

View File

@@ -0,0 +1,52 @@
# vim:ft=kitty
## name: Nightfox
## author: EdenEast
## license: MIT
## upstream: https://github.com/EdenEast/nightfox.nvim/blob/main/extra/nightfox/nightfox_kitty.conf
background #192330
foreground #cdcecf
selection_background #2b3b51
selection_foreground #cdcecf
url_color #81b29a
# Cursor
# uncomment for reverse background
# cursor none
cursor #cdcecf
# Border
active_border_color #719cd6
inactive_border_color #39506d
bell_border_color #f4a261
# Tabs
active_tab_background #719cd6
active_tab_foreground #131a24
inactive_tab_background #2b3b51
inactive_tab_foreground #738091
# normal
color0 #393b44
color1 #c94f6d
color2 #81b29a
color3 #dbc074
color4 #719cd6
color5 #9d79d6
color6 #63cdcf
color7 #dfdfe0
# bright
color8 #575860
color9 #d16983
color10 #8ebaa4
color11 #e0c989
color12 #86abdc
color13 #baa1e2
color14 #7ad5d6
color15 #e4e4e5
# extended colors
color16 #f4a261
color17 #d67ad2

View File

@@ -0,0 +1,32 @@
# One Dark by Giuseppe Cesarano, https://github.com/GiuseppeCesarano
# This work is licensed under the terms of the GPL-2.0 license.
# For a copy, see https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html.
# Colors
foreground #979eab
background #282c34
color0 #282c34
color1 #e06c75
color2 #98c379
color3 #e5c07b
color4 #61afef
color5 #be5046
color6 #56b6c2
color7 #979eab
color8 #393e48
color9 #d19a66
color10 #56b6c2
color11 #e5c07b
color12 #61afef
color13 #be5046
color14 #56b6c2
color15 #abb2bf
# Tab Bar
active_tab_foreground #282c34
active_tab_background #979eab
inactive_tab_foreground #abb2bf
inactive_tab_background #282c34

View File

@@ -0,0 +1,56 @@
## 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
foreground #e0def4
background #232136
selection_foreground #e0def4
selection_background #44415a
cursor #56526e
cursor_text_color #e0def4
url_color #c4a7e7
active_tab_foreground #e0def4
active_tab_background #393552
inactive_tab_foreground #6e6a86
inactive_tab_background #232136
active_border_color #3e8fb0
inactive_border_color #44415a
# black
color0 #393552
color8 #6e6a86
# red
color1 #eb6f92
color9 #eb6f92
# green
color2 #3e8fb0
color10 #3e8fb0
# yellow
color3 #f6c177
color11 #f6c177
# blue
color4 #9ccfd8
color12 #9ccfd8
# magenta
color5 #c4a7e7
color13 #c4a7e7
# cyan
color6 #ea9a97
color14 #ea9a97
# white
color7 #e0def4
color15 #e0def4

View File

@@ -0,0 +1,51 @@
# vim:ft=kitty
## name: Sonokai Sushia
## license: MIT
## author: Dustin Miao
## upstream: https://github.com/selenebun/sonokai-kitty
## blurb: A Kitty port for the Sonokai theme (https://github.com/sainnhe/sonokai)
#: Disclaimer: I am not the original creator of this theme. I have ported it over from
#: https://github.com/selenebun/sonokai-kitty/blob/main/colors/sonokai-shusia.conf.
foreground #e3e1e4
background #2d2a2e
selection_foreground #e3e1e4
selection_background #423f46
cursor #e3e1e4
cursor_text_color background
active_tab_background #2d2a2e
active_tab_foreground #e3e1e4
active_tab_font_style bold
inactive_tab_background #2d2a2e
inactive_tab_foreground #e3e1e4
inactive_tab_font_style normal
color0 #1a181a
color8 #848089
color1 #f85e84
color9 #f85e84
color2 #9ecd6f
color10 #9ecd6f
color3 #e5c463
color11 #e5c463
color4 #7accd7
color12 #7accd7
color5 #ab9df2
color13 #ab9df2
color6 #ef9062
color7 #ef9062
color7 #e3e1e4
color15 #e3e1e4

View File

@@ -0,0 +1,587 @@
// This config is in the KDL format: https://kdl.dev
// "/-" comments out the following node.
// Check the wiki for a full description of the configuration:
// https://yalter.github.io/niri/Configuration:-Introduction
input {
keyboard {
xkb {
layout "ch"
variant "de_mac"
model "apple"
}
}
touchpad {
tap
natural-scroll
}
mouse {
}
trackpoint {
}
}
// x11 support for wayland
xwayland-satellite {
path "xwayland-satellite"
}
environment {
ELECTRON_OZONE_PLATFORM_HINT "auto"
ELECTRON_ENABLE_LOGGING "1"
// GDK_SCALE "1.75"
// GDK_DPI_SCALE "1"
}
output "eDP-1" {
mode "2880x1800@59.990"
scale 1.1
position x=0 y=0
focus-at-startup
}
output "DP-4" {
mode "2560x1440@59.951"
position x=1645 y=0
scale 0.8
}
layout {
gaps 2
center-focused-column "never"
preset-column-widths {
proportion 0.5
proportion 0.66667
proportion 0.3333
}
default-column-width {
proportion 0.5
}
focus-ring {
width 2
active-color "#7daea3"
inactive-color "#928374"
}
border {
off
width 4
active-color "#eba0ac"
inactive-color "#505050"
urgent-color "#9b0000"
}
shadow {
softness 30
spread 5
offset x=0 y=5
color "#0007"
}
struts {
}
}
spawn-at-startup "waybar" "-c" "/home/liph/.config/niri/waybar-niri/config.jsonc" "-s" "/home/liph/.config/niri/waybar-niri/style.css"
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 "kanshi"
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'"
prefer-no-csd
gestures {
hot-corners {
off
}
}
workspace "1"
workspace "2"
workspace "3"
workspace "4"
// Example: block out two password managers from screen capture.
// (This example rule is commented out with a "/-" in front.)
/-window-rule {
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
match app-id=r#"^org\.gnome\.World\.Secrets$"#
block-out-from "screen-capture"
}
// window-rule {
// 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"
open-on-workspace "1"
clip-to-geometry true
}
window-rule {
match at-startup=false app-id="kitty"
clip-to-geometry true
}
// browser window-rules
window-rule {
match at-startup=true app-id="librewolf"
open-on-workspace "2"
open-maximized true
// geometry-corner-radius 5
clip-to-geometry true
}
window-rule {
match at-startup=false app-id="librewolf"
clip-to-geometry true
}
window-rule {
match app-id="Claude"
open-on-workspace "3"
default-column-width {
proportion 0.6667
}
clip-to-geometry true
}
window-rule {
match app-id="floating_btop"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_htop"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_bluetui"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_pulsemixer"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_wavemon"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_network-tui"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
match app-id="floating_yazi"
default-column-width {
proportion 0.6667
}
open-floating true
}
window-rule {
geometry-corner-radius 5
clip-to-geometry true
}
binds {
// Mod+Shift+Slash { show-hotkey-overlay; }
Mod+Mod5+J {
spawn "hyprlock"
}
Mod+Alt+P hotkey-overlay-title="Open Bitwarden" {
spawn "bitwarden-desktop"
}
Mod+Alt+T hotkey-overlay-title="Open Tipp10" {
spawn "tipp10"
}
Mod+Alt+S hotkey-overlay-title="Open Tidal" {
spawn "tidal-hifi" "--ozone-platform=wayland" "--enable-features=WaylandWindowDecorations"
}
Mod+Alt+O hotkey-overlay-title="Open Obsidian" {
spawn "obsidian"
}
Mod+Alt+C hotkey-overlay-title="Open Claude" {
spawn "claude-desktop"
}
Mod+Alt+Z hotkey-overlay-title="Open Librewolf" {
spawn "librewolf"
}
Mod+Alt+Return hotkey-overlay-title="Open Kitty" {
spawn "kitty"
}
Mod+Alt+Space hotkey-overlay-title="Open Wofi" {
spawn "wofi" "--show" "drun"
}
Mod+Mod5+Q hotkey-overlay-title="Open Htop" {
spawn "kitty" "--class" "floating_htop" "-e" "htop"
}
Mod+Mod5+W hotkey-overlay-title="Open Btop" {
spawn "kitty" "--class" "floating_btop" "-e" "btop"
}
Mod+Mod5+E hotkey-overlay-title="Open Pulsemixer" {
spawn "kitty" "--class" "floating_pulsemixer" "-e" "pulsemixer"
}
Mod+Mod5+R hotkey-overlay-title="Open Bluetui" {
spawn "kitty" "--class" "floating_bluetui" "-e" "bluetui"
}
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"
}
Mod+Mod5+Y hotkey-overlay-title="Open Yazi" {
spawn "kitty" "--class" "floating_yazi" "-e" "yazi"
}
// Brightness Controll
Mod+Ctrl+0 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 0"
}
Mod+Ctrl+1 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 1"
}
Mod+Ctrl+2 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 2"
}
Mod+Ctrl+3 {
spawn "sh" "-c" "~/scripts/layer_notify.sh 3"
}
// Brightness controls
Mod+Ctrl+4 {
spawn "sh" "-c" "~/scripts/niri_br_up.sh"
}
Mod+Ctrl+5 {
spawn "sh" "-c" "~/scripts/niri_br_down.sh"
}
Mod+Ctrl+6 {
spawn "sh" "-c" "~/scripts/niri_br_blue.sh"
}
Mod+Ctrl+7 {
spawn "sh" "-c" "~/scripts/niri_br_reset.sh"
}
// Swaync Notifications
Mod+Ctrl+8 {
spawn "swaync-client" "-t"
}
Super+Alt+S allow-when-locked=true hotkey-overlay-title=null {
spawn-sh "pkill orca || exec orca"
}
XF86AudioRaiseVolume allow-when-locked=true {
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+ -l 1.0"
}
XF86AudioLowerVolume allow-when-locked=true {
spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"
}
XF86AudioMute allow-when-locked=true {
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
}
XF86AudioMicMute allow-when-locked=true {
spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
}
XF86AudioPlay allow-when-locked=true {
spawn-sh "playerctl play-pause"
}
XF86AudioStop allow-when-locked=true {
spawn-sh "playerctl stop"
}
XF86AudioPrev allow-when-locked=true {
spawn-sh "playerctl previous"
}
XF86AudioNext allow-when-locked=true {
spawn-sh "playerctl next"
}
XF86MonBrightnessUp allow-when-locked=true {
spawn "brightnessctl" "--class=backlight" "set" "+10%"
}
XF86MonBrightnessDown allow-when-locked=true {
spawn "brightnessctl" "--class=backlight" "set" "10%-"
}
Mod+O repeat=false {
toggle-overview
}
Mod+Shift+C repeat=false {
close-window
}
Mod+Left {
focus-column-left
}
Mod+Down {
focus-window-down
}
Mod+Up {
focus-window-up
}
Mod+Right {
focus-column-right
}
Mod+H {
focus-column-left
}
Mod+J {
focus-window-down
}
Mod+K {
focus-window-up
}
Mod+L {
focus-column-right
}
Mod+Ctrl+Left {
move-column-left
}
Mod+Ctrl+Down {
move-window-down
}
Mod+Ctrl+Up {
move-window-up
}
Mod+Ctrl+Right {
move-column-right
}
Mod+Ctrl+H {
move-column-left
}
Mod+Ctrl+J {
move-window-down
}
Mod+Ctrl+K {
move-window-up
}
Mod+Ctrl+L {
move-column-right
}
// Mod+Shift+Down { focus-monitor-down; }
// Mod+Shift+Up { focus-monitor-up; }
// Mod+Shift+Right { focus-monitor-right; }
Mod+Shift+H {
focus-monitor-left
}
// Mod+Shift+J { focus-monitor-down; }
// Mod+Shift+K { focus-monitor-up; }
Mod+Shift+L {
focus-monitor-right
}
Mod+Shift+Ctrl+Left {
move-column-to-monitor-left
}
// Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
// Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
Mod+Shift+Ctrl+Right {
move-column-to-monitor-right
}
Mod+Shift+Ctrl+H {
move-column-to-monitor-left
}
// Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
// Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
Mod+Shift+Ctrl+L {
move-column-to-monitor-right
}
Mod+U {
focus-workspace-down
}
Mod+I {
focus-workspace-up
}
Mod+Alt+J {
move-column-to-workspace-down
}
Mod+Alt+K {
move-column-to-workspace-up
}
Mod+Alt+U {
move-column-to-workspace-down
}
Mod+Alt+I {
move-column-to-workspace-up
}
Mod+Shift+U {
move-workspace-down
}
Mod+Shift+I {
move-workspace-up
}
Mod+WheelScrollDown cooldown-ms=150 {
focus-workspace-down
}
Mod+WheelScrollUp cooldown-ms=150 {
focus-workspace-up
}
Mod+Ctrl+WheelScrollDown cooldown-ms=150 {
move-column-to-workspace-down
}
Mod+Ctrl+WheelScrollUp cooldown-ms=150 {
move-column-to-workspace-up
}
Mod+WheelScrollRight {
focus-column-right
}
Mod+WheelScrollLeft {
focus-column-left
}
Mod+Ctrl+WheelScrollRight {
move-column-right
}
Mod+Ctrl+WheelScrollLeft {
move-column-left
}
Mod+Shift+WheelScrollDown {
focus-column-right
}
Mod+Shift+WheelScrollUp {
focus-column-left
}
Mod+Ctrl+Shift+WheelScrollDown {
move-column-right
}
Mod+Ctrl+Shift+WheelScrollUp {
move-column-left
}
Mod+1 {
focus-workspace 1
}
Mod+2 {
focus-workspace 2
}
Mod+3 {
focus-workspace 3
}
Mod+4 {
focus-workspace 4
}
Mod+5 {
focus-workspace 5
}
Mod+6 {
focus-workspace 6
}
Mod+7 {
focus-workspace 7
}
Mod+8 {
focus-workspace 8
}
Mod+9 {
focus-workspace 9
}
// Mod+Ctrl+1 { move-column-to-workspace 1; }
// Mod+Ctrl+2 { move-column-to-workspace 2; }
// Mod+Ctrl+3 { move-column-to-workspace 3; }
// Mod+Ctrl+4 { move-column-to-workspace 4; }
// Mod+Ctrl+5 { move-column-to-workspace 5; }
// Mod+Ctrl+6 { move-column-to-workspace 6; }
// Mod+Ctrl+7 { move-column-to-workspace 7; }
// Mod+Ctrl+8 { move-column-to-workspace 8; }
// Mod+Ctrl+9 { move-column-to-workspace 9; }
Mod+BracketLeft {
consume-or-expel-window-left
}
Mod+BracketRight {
consume-or-expel-window-right
}
Mod+Comma {
consume-window-into-column
}
Mod+Period {
expel-window-from-column
}
Mod+R {
switch-preset-column-width
}
// Cycling through the presets in reverse order is also possible.
// Mod+R { switch-preset-column-width-back; }
Mod+Shift+R {
switch-preset-window-height
}
Mod+Ctrl+R {
reset-window-height
}
Mod+F {
maximize-column
}
Mod+Shift+F {
fullscreen-window
}
Mod+Ctrl+F {
expand-column-to-available-width
}
// Mod+C { center-column; }
Mod+Ctrl+C {
center-visible-columns
}
Mod+Minus {
set-column-width "-10%"
}
Mod+Equal {
set-column-width "+10%"
}
// Finer height adjustments when in column with other windows.
Mod+Shift+Minus {
set-window-height "-10%"
}
Mod+Shift+Equal {
set-window-height "+10%"
}
// Move the focused window between the floating and the tiling layout.
Mod+V {
toggle-window-floating
}
Mod+Shift+V {
switch-focus-between-floating-and-tiling
}
Mod+W {
toggle-column-tabbed-display
}
// screenshot
Mod+Alt+4 {
spawn "sh" "-c" "grim -g \"$(slurp)\" ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send 'Screenshot' 'Area screenshot saved'"
}
// Fullscreen and save
Mod+Alt+5 {
spawn "sh" "-c" "grim ~/Pictures/screenshot-$(date +%Y%m%d-%H%M%S).png && notify-send -t 2000 'Screenshot' 'Fullscreen screenshot saved'"
}
// Select area to clipboard
Mod+Shift+4 {
spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy && notify-send -t 2000 'Screenshot' 'Area copied to clipboard'"
}
// area screenshot → annotate in swappy
Mod+Shift+5 {
spawn "sh" "-c" "grim -g \"$(slurp)\" - | swappy -f -"
}
Mod+Escape allow-inhibiting=false {
toggle-keyboard-shortcuts-inhibit
}
Mod+Shift+E {
quit
}
Ctrl+Alt+Delete {
quit
}
Mod+Shift+P {
power-off-monitors
}
}
// include "theme.kdl"

View File

@@ -0,0 +1,45 @@
.base {
color: @base;
}
.surface {
color: @surface;
}
.overlay {
color: @overlay;
}
.muted {
color: @muted;
}
.subtle {
color: @subtle;
}
.text {
color: @text;
}
.red {
color: @love;
}
.yellow {
color: @gold;
}
.green {
color: @rose;
}
.rose {
color: @pine;
}
.blue {
color: @foam;
}
.sky {
color: @iris;
}
.highlightLow {
color: @highlightLow;
}
.highlightMed {
color: @highlightMed;
}
.highlightHigh {
color: @highlightHigh;
}

View File

@@ -0,0 +1,139 @@
{
"layer": "top",
"position": "top",
"height": 30,
"spacing": 0,
"modules-left": ["custom/logo", "cpu", "memory", "bluetooth"],
"modules-center": ["niri/workspaces"],
"modules-right": [
"tray",
"power-profiles-daemon",
// "niri/language",
"pulseaudio",
"network",
"battery",
"clock",
"custom/power",
],
"niri/workspaces": {
"format": "{icon}",
"format-icons": {
// Named workspaces
// (you need to configure them in niri)
// "1",
// "2": "2",
// "3": "3",
// "4": "4",
// "5": "5",
// "6": "6",
// Icons by state
"active": "",
"default": "",
},
},
// "mpris": {
// "format": "DEFAULT: {player_icon} {dynamic}",
// "format-paused": "DEFAULT: {status_icon} <i>{dynamic}</i>",
// "player-icons": {
// "default": "▶",
// "mpv": "🎵",
// },
// "status-icons": {
// "paused": "⏸",
// },
// },
"tray": {
"icon-size": 14,
"spacing": 10,
},
"clock": {
"format": "{:%H:%M:%S}",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"format-alt": "{:%Y-%m-%d}",
"interval": 1,
},
"cpu": {
"format": "<span font='14' > </span>{usage}%",
"tooltip": false,
"interval": 1,
"on-click": "kitty --class floating_btop -e btop",
},
"memory": {
"format": "<span font='14'>  </span>{}%",
// "format": "<span font='14' class='red'>  </span> {}%",
"interval": 1,
"on-click": "kitty --class floating_btop -e htop",
},
"battery": {
"interval": 1,
"states": {
"warning": 30,
"critical": 15,
},
"format": "<span font='14' >{icon}</span> {capacity}%",
"format-full": "<span font='14' >{icon}</span> {capacity}%",
"format-charging": "<span font='14' >󰂄</span> {capacity}%",
"format-plugged": "<span font='14' >󰂄</span> {capacity}%",
"format-icons": ["󰁺", "󰁼", "󰁾", "󰂀", "󰂂", "󰁹"],
},
"power-profiles-daemon": {
"format": "<span >{icon}</span>",
"tooltip-format": "Power profile: {profile}\nDriver: {driver}",
"tooltip": true,
"format-icons": {
"default": " ",
"performance": " ",
"balanced": " ",
"power-saver": "󰌪 ",
},
},
"network": {
"format-wifi": "<span font='14' >󰖩</span> ",
"format-ethernet": "<span font='14' >󰈀</span>",
"format-disconnected": "<span font='14' >󱚵</span>",
"format-alt": "{bandwidthDownBytes}",
"interval": 1,
// "on-click": "kitty --class floating_wavemon -e sudo wavemon",
},
"pulseaudio": {
"format": "<span font='14' >{icon}</span> {volume}%",
"format-muted": "<span font='14'>󰝟</span>",
"format-source": "{volume}% ",
"format-source-muted": "",
"format-icons": {
"headphone": "",
// "hands-free": "",
// "headset": "",
// "phone": "",
// "portable": "",
// "car": "",
"default": ["󰕿", "󰖀", "󰕾"],
},
"on-click": "kitty --class floating_pulsemixer -e pulsemixer",
},
"custom/power": {
"format": "⏻",
"tooltip": false,
"on-click": "wlogout -l ~/.config/niri/wlogout/layout",
},
"custom/logo": {
"format": "<span font='14'>󰣇</span>",
"on-click": "wofi --show drun",
},
"bluetooth": {
"format": "<span font='14'> </span>",
"format-disabled": "<span font='14'> </span>",
"format-connected": "<span font='14'> </span>",
"tooltip-format": "{controller_alias}\t{controller_address}\n\n{num_connections} connected",
"tooltip-format-connected": "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}",
"tooltip-format-enumerate-connected": "{device_alias}\t{device_address}",
"tooltip-format-enumerate-connected-battery": "{device_alias}\t{device_address}\t{device_battery_percentage}%",
"on-click": "kitty --class floating_bluetui -e bluetui",
},
// "bluetooth": {
// "format": "<span font='14'>󰂯</span>",
// "format-disabled": "<span font='14'>󰂲</span>",
// "format-connected": "<span font='14'>󰂱</span>",
// "on-click": "kitty --class floating_bluetui -e bluetui",
// },
}

View File

@@ -0,0 +1,139 @@
@import "theme.css";
@import "colors.css";
* {
font-family: "JetBrainsMono";
font-size: 12px;
}
window#waybar {
background-color: @base;
/* background: radial-gradient(alpha(@theme_base_color, 0.4), @theme_base_color); */
color: @theme_fg_color;
transition-property: background-color;
transition-duration: 0.5s;
}
window#waybar.hidden {
/* opacity: 0.2; */
}
button {
border: none;
border-radius: 0;
}
button:hover {
/* background: inherit; */
}
#workspaces {
padding: 5px;
}
#workspaces button {
padding: 0 2px;
background-color: transparent;
color: @theme_fg_color;
border-radius: 10px;
}
#workspaces button:hover {
color: @gold;
}
#workspaces button.active,
#workspaces button.default,
#workspaces button.focused {
background: @rose;
}
#workspaces button.urgent {
background-color: @love;
}
#clock,
#battery,
#cpu,
#memory,
#disk,
#temperature,
#backlight,
#network,
#pulseaudio,
#wireplumber,
#custom-media,
#tray,
#mode,
#idle_inhibitor,
#scratchpad,
#power-profiles-daemon,
#mpd {
padding: 0 10px;
color: @theme_fg_color;
}
#window,
#workspaces {
margin: 0 4px;
}
/* If workspaces is the leftmost module, omit left margin */
.modules-left > widget:first-child > #workspaces {
margin-left: 0;
}
/* If workspaces is the rightmost module, omit right margin */
.modules-right > widget:last-child > #workspaces {
margin-right: 0;
}
#clock {
color: @text;
}
#battery {
color: @iris;
}
#cpu {
color: @iris;
}
#memory {
color: @foam;
}
#backlight,
#network {
color: @gold;
}
#pulseaudio {
color: @rose;
}
#custom-media,
#tray,
#custom-power,
#bluetooth,
#power-profiles-daemon {
padding: 5px 15px;
color: @theme_text_color;
background-color: alpha(@theme_unfocused_bg_color, 0.4);
border-radius: 20px;
margin: 0 4px;
}
#custom-power {
background-color: @love;
border-radius: 20px 0 0 20px;
transition: all 250ms ease-in-out;
}
#battery.charging {
color: #00ff1c;
}
#clock {
border-radius: 20px;
margin: 0 5px;
}
#pulseaudio.muted {
}
#network.disconnected {
}
#tray {
border-radius: 20px;
margin: 0 5px;
}
#custom-logo {
background-color: @pine;
padding: 0 17px;
border-radius: 0 20px 20px 0;
}
#custom-power {
margin: 0;
}
#bluetooth {
border-radius: 20px;
margin: 0 5px;
color: @pine;
}

View File

@@ -0,0 +1,15 @@
@define-color base #191724;
@define-color surface #1f1d2e;
@define-color overlay #26233a;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color love #eb6f92;
@define-color gold #f6c177;
@define-color rose #ea9a97;
@define-color pine #3e8fb0;
@define-color foam #9ccfd8;
@define-color iris #c4a7e7;
@define-color highlightLow #21202e;
@define-color highlightMed #403d52;
@define-color highlightHigh #524f67;

View File

@@ -0,0 +1,26 @@
@define-color rosewater #f5e0dc;
@define-color flamingo #f2cdcd;
@define-color pink #f5c2e7;
@define-color mauve #cba6f7;
@define-color red #f38ba8;
@define-color maroon #eba0ac;
@define-color rose #fab387;
@define-color yellow #f9e2af;
@define-color green #a6e3a1;
@define-color teal #94e2d5;
@define-color sky #89dceb;
@define-color sapphire #74c7ec;
@define-color blue #89b4fa;
@define-color lavender #b4befe;
@define-color text #cdd6f4;
@define-color subtext1 #bac2de;
@define-color subtext0 #a6adc8;
@define-color overlay2 #9399b2;
@define-color overlay1 #7f849c;
@define-color overlay #6c7086;
@define-color highlightedHigh #585b70;
@define-color highlightedMed #45475a;
@define-color highlightedlow #313244;
@define-color base #1e1e2e;
@define-color mantle #181825;
@define-color crust #11111b;

View File

@@ -0,0 +1,16 @@
/*
@define-color base #232136;
@define-color surface #2a273f;
@define-color overlay #393552;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color red #eb6f92;
@define-color yellow #f6c177;
@define-color rose #ea9a97;
@define-color blue #3e8fb0;
@define-color sky #9ccfd8;
@define-color mauve #c4a7e7;
@define-color highlightLow #2a283e;
@define-color highlightMed #44415a;
@define-color highlightHigh #56526e;

View File

@@ -0,0 +1,23 @@
/*
* Variant: Rosé Pine
* Maintainer: DankChoir
*/
@define-color base #191724;
@define-color surface #1f1d2e;
@define-color overlay #26233a;
@define-color muted #6e6a86;
@define-color subtle #908caa;
@define-color text #e0def4;
@define-color love #eb6f92;
@define-color gold #f6c177;
@define-color rose #ebbcba;
@define-color pine #31748f;
@define-color foam #9ccfd8;
@define-color iris #c4a7e7;
@define-color highlightLow #21202e;
@define-color highlightMed #403d52;
@define-color highlightHigh #524f67;

View File

@@ -0,0 +1,36 @@
{
"label": "lock",
"action": "hyprlock",
"text": "Lock",
"keybind": "l"
}
{
"label": "logout",
"action": "niri msg action quit",
"text": "Logout",
"keybind": "e"
}
{
"label": "suspend",
"action": "systemctl suspend",
"text": "Suspend",
"keybind": "u"
}
{
"label": "hibernate",
"action": "systemctl hibernate",
"text": "Hibernate",
"keybind": "h"
}
{
"label": "shutdown",
"action": "systemctl poweroff",
"text": "Shutdown",
"keybind": "s"
}
{
"label": "reboot",
"action": "systemctl reboot",
"text": "Reboot",
"keybind": "r"
}

View File

@@ -6,6 +6,7 @@
],
"runtime.unicodeName": true,
"diagnostics.disable": [
"undefined-doc-name"
"undefined-doc-name",
"assign-type-mismatch"
]
}

View File

@@ -0,0 +1,7 @@
-- Tell Neovim to treat .ltx as regular tex
vim.filetype.add({
extension = {
ltx = "tex",
tex = "tex",
},
})

View File

@@ -0,0 +1,89 @@
-- after/ftplugin/html.lua (or just :source % once)
local port = 8085
local root = vim.loop.cwd()
local job_id = nil
vim.api.nvim_create_user_command("Serve", function()
-- kill previous python instance
if job_id then
vim.fn.jobstop(job_id)
end
-- one-liner HTTP server + websocket broadcast
local script = ([[
import http.server, os, subprocess, threading, time, mimetypes, json
from http.server import HTTPServer, SimpleHTTPRequestHandler
import websockets, asyncio, pathlib
ROOT = %q
PORT = %d
clients = set()
class Handler(SimpleHTTPRequestHandler):
def log_message(self, fmt, *args): pass # shut up
def do_GET(self):
if self.path == '/':
self.path = '/index.html'
# inject tiny reload script into every HTML
if self.path.endswith('.html'):
ctype = 'text/html; charset=utf-8'
with open(os.path.join(ROOT, self.path.lstrip('/')), 'rb') as f:
content = f.read().decode()
inject = '<script>let ws=new WebSocket("ws://localhost:%d/ws");ws.onmessage=()=>location.reload()</script>'
content = content.replace('</body>', inject .. '</body>')
self.send_response(200)
self.send_header('Content-type', ctype)
self.end_headers()
self.wfile.write(content.encode())
else:
super().do_GET()
async def ws_handler(websocket):
clients.add(websocket)
try:
await websocket.wait_closed()
finally:
clients.remove(websocket)
async def websocket_main():
await websockets.serve(ws_handler, "localhost", %d, path="/ws")
def broadcast_reload():
for c in clients:
asyncio.run(c.send("reload"))
clients.clear()
def reload_loop():
while True:
time.sleep(0.05)
try:
subprocess.check_call(["inotifywait", "-e", "close_write", "-t", "0", ROOT],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
broadcast_reload()
except: pass # inotify not installed → noop (manual F5 still works)
def main():
threading.Thread(target=reload_loop, daemon=True).start()
asyncio.set_event_loop(asyncio.new_event_loop())
threading.Thread(target=lambda: asyncio.run(websocket_main()), daemon=True).start()
HTTPServer(("", PORT), Handler).serve_forever()
if __name__ == '__main__':
main()
]]):format(root, port, port, port)
job_id = vim.fn.jobstart({ "python", "-c", script }, { stdout_buffered = true })
vim.fn.jobstart({ "sleep", "0.2" }, {
on_exit = function()
vim.ui.open(("http://localhost:%d"):format(port))
end,
})
vim.notify("Serving → http://localhost:" .. port, vim.log.levels.INFO)
end, {})
vim.api.nvim_create_user_command("ServeStop", function()
if job_id then
vim.fn.jobstop(job_id)
job_id = nil
end
end, {})

View File

@@ -0,0 +1,126 @@
-- Helper function to extract clean email address
local function extract_email(address_string)
-- Match email in angle brackets: "Name <email@example.com>"
local email = address_string:match("<([^>]+)>")
if email then
return email
end
-- Match standalone email: "email@example.com"
email = address_string:match("([%w%._%+-]+@[%w%._%+-]+)")
if email then
return email
end
-- Fallback: return as-is if no pattern matches
return address_string
end
-- Option 2: Omnifunc completion
function _G.mail_complete(findstart, base)
if findstart == 1 then
-- Find start of word
local line = vim.api.nvim_get_current_line()
local col = vim.fn.col(".")
local line_to_cursor = line:sub(1, col - 1)
-- Only on header lines
if not line_to_cursor:match("^%s*[ToCcBcFrom]+:%s*") then
return -1
end
-- Find start of current address
local start = line_to_cursor:reverse():find("[%s,]")
if start then
return col - start
else
return line_to_cursor:find(":") or 0
end
else
-- Get completions
local handle = io.popen(string.format('abook --mutt-query "%s" 2>/dev/null | tail -n +2', base))
if not handle then
return {}
end
local result = handle:read("*a")
handle:close()
local matches = {}
for address in result:gmatch("[^\r\n]+") do
if address ~= "" then
-- Extract clean email only
local clean_email = extract_email(address)
table.insert(matches, clean_email)
end
end
return matches
end
end
-- Set omnifunc
vim.bo.omnifunc = "v:lua.mail_complete"
-- Map Tab to trigger completion on header lines
vim.keymap.set("i", "<Tab>", function()
local line = vim.api.nvim_get_current_line()
if line:match("^%s*[ToCcBcFrom]+:%s*") then
return "<C-x><C-o>"
else
return "<Tab>"
end
end, { expr = true, buffer = true })
-- Option 3: Interactive fzf picker
local function pick_email()
-- Get all addresses
local handle = io.popen('abook --mutt-query "" 2>/dev/null | tail -n +2')
if not handle then
vim.notify("Could not query abook", vim.log.levels.ERROR)
return
end
local addresses = {}
local display_addresses = {}
for line in handle:lines() do
if line ~= "" then
local clean_email = extract_email(line)
table.insert(addresses, clean_email)
-- Show full format in picker, but insert clean email
table.insert(display_addresses, line .. "" .. clean_email)
end
end
handle:close()
if #addresses == 0 then
vim.notify("No addresses in abook", vim.log.levels.WARN)
return
end
-- Use vim.ui.select (works with fzf-lua if installed)
vim.ui.select(display_addresses, {
prompt = "Select recipient:",
format_item = function(item)
return item
end,
}, function(choice, idx)
if choice and idx then
local email = addresses[idx]
-- Insert at cursor
local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local line = vim.api.nvim_get_current_line()
local new_line = line:sub(1, col) .. email .. line:sub(col + 1)
vim.api.nvim_set_current_line(new_line)
-- Move cursor after inserted text
vim.api.nvim_win_set_cursor(0, { row, col + #email })
end
end)
end
-- Map Ctrl+f to pick email (Option 3)
vim.keymap.set("i", "<C-f>", pick_email, { buffer = true, desc = "Pick email address with fzf" })
-- Optional: Map Ctrl+a for Tab completion manually (in case Tab doesn't work)
vim.keymap.set("i", "<C-a>", "<C-x><C-o>", { buffer = true, desc = "Trigger address completion" })

View File

@@ -1,3 +1,6 @@
-- In your init.lua or after lazy setup
vim.opt.termguicolors = true
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
@@ -11,17 +14,37 @@ if not vim.loop.fs_stat(lazypath) then
end
vim.opt.rtp:prepend(lazypath)
-- Enable spell check for mail files
vim.api.nvim_create_autocmd("FileType", {
pattern = "mail",
callback = function()
vim.opt_local.spell = true
vim.opt_local.spelllang = "en_us,de" -- Add languages you need
end,
})
-- Only enable spell checking for specific filetypes
vim.api.nvim_create_autocmd("FileType", {
pattern = { "markdown", "text", "gitcommit", "tex" },
pattern = { "mail", "markdown", "text", "gitcommit", "tex", "plaintext" },
callback = function()
vim.opt_local.spell = true
vim.opt_local.spelllang = "en_us,de"
end,
})
vim.filetype.add({
extension = {
tex = "tex",
},
})
require("vim-options")
require("lazy").setup("plugins")
-- SINGLE lazy.setup call - combine everything here
require("lazy").setup({
-- Load plugins from the plugins directory
{ import = "plugins" },
})
require("lualine").setup({
sections = {
lualine_x = {
@@ -33,10 +56,18 @@ require("lualine").setup({
},
},
})
require("lazy").setup({
{
"tzachar/cmp-tabnine",
build = "./install.sh",
dependencies = "hrsh7th/nvim-cmp",
},
})
-- In your init.lua or after lazy setup
local theme_file = vim.fn.stdpath("config") .. "/lua/current_theme.lua"
if vim.fn.filereadable(theme_file) == 1 then
dofile(theme_file)
else
vim.cmd.colorscheme("catppuccin") -- fallback
end
vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
vim.api.nvim_set_hl(0, "NormalNC", { bg = "none" })
vim.api.nvim_set_hl(0, "SignColumn", { bg = "none" })
vim.api.nvim_set_hl(0, "FloatBorder", { bg = "none" })
vim.api.nvim_set_hl(0, "LineNr", { bg = "none" })

View File

@@ -1,60 +1,76 @@
{
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
"LuaSnip": { "branch": "master", "commit": "fb525166ccc30296fb3457441eb979113de46b00" },
"LuaSnip": { "branch": "master", "commit": "5a1e39223db9a0498024a77b8441169d260c8c25" },
"R.nvim": { "branch": "main", "commit": "659dc18aba2d2b799c04efbce66a15ea87bd6ec6" },
"barbecue": { "branch": "main", "commit": "cd7e7da622d68136e13721865b4d919efd6325ed" },
"catppuccin": { "branch": "main", "commit": "ce8d176faa4643e026e597ae3c31db59b63cef09" },
"catppuccin": { "branch": "main", "commit": "0a5de4da015a175f416d6ef1eda84661623e0500" },
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
"cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
"cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" },
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
"cmp-tabnine": { "branch": "main", "commit": "c0167cdc86c15e782c5461ee62aebee89231c2ed" },
"cmp-r": { "branch": "main", "commit": "70bfe8f4c062acc10266e24825439c009a0b1b89" },
"cmp-vimtex": { "branch": "master", "commit": "5283bf9108ef33d41e704027b9ef22437ce7a15b" },
"cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
"conform.nvim": { "branch": "master", "commit": "5f5152fae39a57e3a04cbce09fb21e10c49c9d95" },
"conform.nvim": { "branch": "master", "commit": "e969e302bced7ffb9a0a0323629f31feb0ca35a6" },
"dashboard-nvim": { "branch": "master", "commit": "0775e567b6c0be96d01a61795f7b64c1758262f6" },
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
"dracula.nvim": { "branch": "main", "commit": "ae752c13e95fb7c5f58da4b5123cb804ea7568ee" },
"dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
"everforest": { "branch": "master", "commit": "b03a03148c8b34c24c96960b93da9c8883d11f54" },
"flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" },
"friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" },
"fzf-lua": { "branch": "main", "commit": "9d579feab4d3035627150e5e9b6e8fbf5e814ef6" },
"gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" },
"friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
"fzf-lua": { "branch": "main", "commit": "76ddd6b56875bf4f6fd13848728207e738bb75a3" },
"gitsigns.nvim": { "branch": "main", "commit": "9f3c6dd7868bcc116e9c1c1929ce063b978fa519" },
"grapple.nvim": { "branch": "main", "commit": "b41ddfc1c39f87f3d1799b99c2f0f1daa524c5f7" },
"harpoon": { "branch": "harpoon2", "commit": "87b1a3506211538f460786c23f98ec63ad9af4e5" },
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
"gruvbox-mat": { "branch": "master", "commit": "790afe9dd085aa04eccd1da3626c5fa05c620e53" },
"indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
"kanagawa.nvim": { "branch": "master", "commit": "aef7f5cec0a40dbe7f3304214850c472e2264b10" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"lazydev.nvim": { "branch": "main", "commit": "5231c62aa83c2f8dc8e7ba957aa77098cda1257d" },
"live-preview.nvim": { "branch": "main", "commit": "4bbcbe79948b2726f00b5217277abd1e2651e699" },
"lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" },
"luvit-meta": { "branch": "main", "commit": "0ea4ff636c5bb559ffa78108561d0976f4de9682" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "8f1a3e6eecb638817e8999aaa16ada27cd54d867" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
"mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "6c4830e37743b060d13c9269394176aea6a0fbc8" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" },
"neo-tree.nvim": { "branch": "v3.x", "commit": "f3df514fff2bdd4318127c40470984137f87b62e" },
"neogit": { "branch": "master", "commit": "d8bf9102692250193b855acd9025a826f1af2729" },
"neogit": { "branch": "master", "commit": "0ea6b87ae19e8e5a235c682f8db1305774824f9b" },
"nightfox.nvim": { "branch": "main", "commit": "ba47d4b4c5ec308718641ba7402c143836f35aa9" },
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
"nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" },
"nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" },
"nvim-lint": { "branch": "master", "commit": "7a64f4067065c16a355d40d0d599b8ca6b25de6d" },
"nvim-lspconfig": { "branch": "master", "commit": "d20d83b3f24f5884da73a9fc92fdc47e778b8d0d" },
"nvim-navic": { "branch": "master", "commit": "7d914a39a1ef8f4e22c2c4381abeef7c556f5a13" },
"nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
"nvim-cmp": { "branch": "main", "commit": "da88697d7f45d16852c6b2769dc52387d1ddc45f" },
"nvim-colorizer.lua": { "branch": "master", "commit": "a065833f35a3a7cc3ef137ac88b5381da2ba302e" },
"nvim-lint": { "branch": "master", "commit": "486474c2ace8d78d28995074dbdbe29011bc63d0" },
"nvim-lspconfig": { "branch": "master", "commit": "5a855bcfec7973767a1a472335684bbd71d2fa2b" },
"nvim-navic": { "branch": "master", "commit": "f5eba192f39b453675d115351808bd51276d9de5" },
"nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" },
"nvim-spectre": { "branch": "master", "commit": "72f56f7585903cd7bf92c665351aa585e150af0f" },
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
"nvim-surround": { "branch": "main", "commit": "b2f5ff27e603e0a756bd1926c476d2f219e6d42a" },
"nvim-tmux-navigation": { "branch": "main", "commit": "4898c98702954439233fdaf764c39636681e2861" },
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
"nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
"nvim-treesitter": { "branch": "main", "commit": "dc42c209f3820bdfaae0956f15de29689aa6b451" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "a0e182ae21fda68c59d1f36c9ed45600aef50311" },
"nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" },
"nvim-web-devicons": { "branch": "master", "commit": "6788013bb9cb784e606ada44206b0e755e4323d7" },
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
"nvim-web-devicons": { "branch": "master", "commit": "746ffbb17975ebd6c40142362eee1b0249969c5c" },
"obsidian.nvim": { "branch": "main", "commit": "ae1f76a75c7ce36866e1d9342a8f6f5b9c2caf9b" },
"onedark-warm": { "branch": "master", "commit": "213c23ae45a04797572242568d5d51937181792d" },
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
"portal.nvim": { "branch": "main", "commit": "77d9d53fec945bfa407d5fd7120f1b4f117450ed" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "8aafe2cbd89cd4090f573a98cab6b20366576fde" },
"render-markdown.nvim": { "branch": "main", "commit": "07d088bf8bdadd159eb807b90eaee86a4778383f" },
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
"rainbow-delimiters.nvim": { "branch": "master", "commit": "e2858c43caafec291f915ef25fbed27bbe78abbd" },
"render-markdown.nvim": { "branch": "main", "commit": "1c958131c083c8557ea499fdb08c88b8afb05c4e" },
"rose-pine": { "branch": "main", "commit": "cf2a288696b03d0934da713d66c6d71557b5c997" },
"snacks.nvim": { "branch": "main", "commit": "fe7cfe9800a182274d0f868a74b7263b8c0c020b" },
"sonokai": { "branch": "master", "commit": "b023c5280b16fe2366f5e779d8d2756b3e5ee9c3" },
"telescope-ui-select.nvim": { "branch": "master", "commit": "6e51d7da30bd139a6950adf2a47fda6df9fa06d2" },
"telescope.nvim": { "branch": "master", "commit": "e709d31454ee6e6157f0537f861f797bd44c0bad" },
"telescope.nvim": { "branch": "master", "commit": "5255aa27c422de944791318024167ad5d40aad20" },
"todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
"toggleterm.nvim": { "branch": "main", "commit": "50ea089fc548917cc3cc16b46a8211833b9e3c7c" },
"tokyonight": { "branch": "main", "commit": "5da1b76e64daf4c5d410f06bcb6b9cb640da7dfd" },
"trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" },
"vim-test": { "branch": "master", "commit": "aa619692ff48a3cf3e6bdb893765039488d4e5f3" },
"vimux": { "branch": "master", "commit": "614f0bb1fb598f97accdcea71d5f7b18d7d62436" },
"vimtex": { "branch": "master", "commit": "95b93a24740f7b89dd8331326b41bdd1337d79f6" },
"which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" },
"yazi.nvim": { "branch": "main", "commit": "d87cd885e06d50e1bb5607e48d96c1094d3b10e3" }
"yazi.nvim": { "branch": "main", "commit": "854ac9c225fcfd8f0c8711819783366814da40eb" }
}

View File

@@ -0,0 +1,10 @@
-- 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

@@ -0,0 +1,45 @@
-- ~/.config/nvim/lua/plugins/blink.lua
return {
"saghen/blink.cmp",
version = "1.*",
build = "cargo build --release",
lazy = false,
dependencies = {
"L3MON4D3/LuaSnip",
"rafamadriz/friendly-snippets",
},
opts = {
fuzzy = { implementation = "prefer_rust" },
sources = {
min_keyword_length = 2,
default = { "lsp", "snippets", "buffer", "path", "lua" },
per_filetype = {
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
},
},
completion = {
accept = { auto_brackets = { enabled = true } },
trigger = { show_on_trigger_character = true },
list = {
selection = {
preselect = function(ctx) return ctx.mode ~= "cmdline" end,
},
},
documentation = {
auto_show = true,
auto_show_delay_ms = 100,
},
},
signature = { enabled = true, window = { border = "rounded" } },
cmdline = { enabled = false },
},
config = function(_, opts)
require("blink.cmp").setup(opts)
end,
}

View File

@@ -0,0 +1,60 @@
-- ~/.config/nvim/lua/plugins/blink.lua
return {
"saghen/blink.cmp",
version = "1.*",
build = "cargo build --release",
lazy = false,
dependencies = {
"L3MON4D3/LuaSnip",
"rafamadriz/friendly-snippets",
},
opts = {
fuzzy = { implementation = "prefer_rust" },
sources = {
min_keyword_length = 1,
default = { "lsp", "snippets", "buffer", "path" },
providers = {
lsp = { name = "LSP", opts = { max_items = 200, score_offset = 0 } },
buffer = { name = "Buffer", opts = { max_items = 100 } },
},
per_filetype = {
lua = { default = { "lazydev", "lsp", "snippets", "buffer", "path" } },
},
},
completion = {
accept = { auto_brackets = { enabled = true } },
trigger = { show_on_trigger_character = true },
list = { selection = "auto_insert" },
documentation = { auto_show = true, auto_show_delay_ms = 200 },
ghost_text = { enabled = true },
},
signature = { enabled = true, window = { border = "rounded" } },
cmdline = { enabled = false },
-- Tab cycles through items and accepts
keymap = {
preset = "enter",
["<Tab>"] = {
"select_next",
"accept",
"snippet_forward",
"fallback",
},
["<S-Tab>"] = {
"select_prev",
"snippet_backward",
"fallback",
},
["<CR>"] = { "accept", "fallback" },
},
},
config = function(_, opts)
require("blink.cmp").setup(opts)
end,
}

View File

@@ -1,9 +0,0 @@
return {
{
"hrsh7th/cmp-path", -- file and folders
"hrsh7th/cmp-buffer", -- words from the current buffer
"hrsh7th/cmp-nvim-lsp", -- LSP-based autocompletions
"hrsh7th/cmp-cmdline", -- Command-line autocompletions
"tzachar/cmp-tabnine"--, build = "./install.sh",
},
}

View File

@@ -0,0 +1,61 @@
-- completions.lua: Provides intelligent code completion via LSP or snippet engines.
return {
{
"hrsh7th/cmp-nvim-lsp",
},
{
"hrsh7th/cmp-cmdline", -- Command line completion
},
{
"hrsh7th/cmp-nvim-lua", -- Better Neovim Lua API completion
},
{
"hrsh7th/cmp-path", -- ADD THIS: Path completion source
},
{
"hrsh7th/cmp-buffer", -- ADD THIS: Buffer completion source
},
{
"L3MON4D3/LuaSnip",
version = "v2.*",
build = "make install_jsregexp",
dependencies = {
"rafamadriz/friendly-snippets", -- Pre-made snippets
},
},
{
"hrsh7th/nvim-cmp",
config = function()
local cmp = require("cmp")
require("luasnip.loaders.from_vscode").lazy_load()
cmp.setup({
snippet = {
expand = function(args)
require("luasnip").lsp_expand(args.body)
end,
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "nvim_lua" }, -- ADD THIS: For nvim lua API completion
}, {
{ name = "buffer" },
{ name = "path" },
{ name = "cmp_tabnine" }, -- ai powered suggestions
}),
})
end,
},
}

View File

@@ -0,0 +1,65 @@
-- completions.lua: Provides intelligent code completion via LSP or snippet engines.
-- completion.lua (nvim-cmp + LuaSnip)
return {
------------------------------------------------------------------
-- 1. Snippet engine & pre-made snippets
------------------------------------------------------------------
{
"L3MON4D3/LuaSnip",
version = "v2.*",
build = "make install_jsregexp",
dependencies = { "rafamadriz/friendly-snippets" },
},
------------------------------------------------------------------
-- 2. Core completion engine + standard sources
------------------------------------------------------------------
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"hrsh7th/cmp-cmdline",
"hrsh7th/cmp-nvim-lua",
"micangl/cmp-vimtex",
},
------------------------------------------------------------------
-- 3. nvim-cmp setup
------------------------------------------------------------------
config = function()
local cmp = require("cmp")
require("luasnip.loaders.from_vscode").lazy_load()
cmp.setup({
snippet = {
expand = function(args)
require("luasnip").lsp_expand(args.body)
end,
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
mapping = cmp.mapping.preset.insert({
["<Tab>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
["<S-Tab>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<C-e>"] = cmp.mapping.abort(),
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
}),
sources = cmp.config.sources({
{ name = "vimtex" },
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "nvim_lua" },
}, {
{ name = "buffer" },
{ name = "path" },
}),
})
-- cmd-line completion
cmp.setup.cmdline({ "/", "?" }, { sources = { { name = "buffer" } } })
cmp.setup.cmdline(":", { sources = cmp.config.sources({ { name = "path" } }, { { name = "cmdline" } }) })
end,
},
}

View File

@@ -0,0 +1,92 @@
-- lua/plugins/dap.lua
return {
{ "mfussenegger/nvim-dap" }, -- core debugger client
{ "rcarriga/nvim-dap-ui", dependencies = { "mfussenegger/nvim-dap" } },
-- Adapter bridge (Mason downloads the compiled server for us)
{
"mxsdev/nvim-dap-vscode-js",
dependencies = { "mfussenegger/nvim-dap" },
config = function()
local mason_js = vim.fn.stdpath "data"
.. "/mason/packages/js-debug-adapter" -- Mason path
require("dap-vscode-js").setup {
node_path = "node", -- node binary
debugger_path = mason_js, -- root dir of js-debug
adapters = { "pwa-node","pwa-chrome","pwa-msedge","node-terminal" }
}
end
},
-- Ensure js-debug-adapter is present (Mason does the download)
{
"williamboman/mason.nvim",
cmd = { "Mason" },
config = true,
},
{
"williamboman/mason-lspconfig.nvim", -- pull Mason registry
opts = function(_, o)
o.ensure_installed = o.ensure_installed or {}
vim.list_extend(o.ensure_installed, { "js-debug-adapter" })
end
},
-- Optional key-maps (adapt to your taste)
{
"mfussenegger/nvim-dap",
keys = {
{ "<F5>", function() require("dap").continue() end, desc="Start / Continue" },
{ "<F9>", function() require("dap").toggle_breakpoint() end, desc="Toggle BP" },
{ "<F10>", function() require("dap").step_over() end, desc="Step over" },
{ "<F11>", function() require("dap").step_into() end, desc="Step into" },
{ "<S-F11>",function() require("dap").step_out() end, desc="Step out" },
}
},
-- Debugger UI (optional)
{
"rcarriga/nvim-dap-ui",
opts = {},
keys = {
{ "<leader>du", function() require("dapui").toggle() end, desc="Dap-UI" },
}
}
}
-- -------------------------------------------------------------
-- Launch / attach configurations
-- -------------------------------------------------------------
local dap = require "dap"
dap.configurations.javascript = {
-- 1. Run current file with node
{
name = "Launch Node file",
type = "pwa-node",
request = "launch",
program = "${file}",
cwd = "${workspaceFolder}",
stopOnEntry = false,
},
-- 2. Attach to an existing node process (started with --inspect)
{
name = "Attach to node process",
type = "pwa-node",
request = "attach",
processId = require("dap.utils").pick_process
},
-- 3. Launch Chrome and attach to page
{
name = "Launch Chrome & attach",
type = "pwa-chrome",
request = "launch",
url = "http://localhost:8080",
webRoot = "${workspaceFolder}",
userDataDir = false -- reuse your normal profile
}
}
-- share same configs for TS
dap.configurations.typescript = dap.configurations.javascript
dap.configurations.typescriptreact = dap.configurations.javascript

View File

@@ -0,0 +1,46 @@
-- dressing.lua: Enhances UI elements like input boxes and select menus for consistency.
return {
{
"stevearc/dressing.nvim",
event = "VeryLazy",
opts = {
input = {
enabled = true,
default_prompt = "Input:",
trim_prompt = true,
title_pos = "left",
start_in_insert = true,
border = "rounded",
relative = "cursor",
prefer_width = 40,
win_options = {
winblend = 0,
},
mappings = {
n = {
["<Esc>"] = "Close",
["<CR>"] = "Confirm",
},
i = {
["<C-c>"] = "Close",
["<CR>"] = "Confirm",
["<Up>"] = "HistoryPrev",
["<Down>"] = "HistoryNext",
},
},
},
select = {
enabled = true,
backend = { "telescope", "builtin" },
trim_prompt = true,
telescope = require("telescope.themes").get_dropdown({
layout_config = {
width = 0.8,
height = 0.8,
},
}),
},
},
},
}

View File

@@ -0,0 +1,71 @@
return {
"olimorris/emmet-ls",
ft = {
"html",
"htm",
"css",
"scss",
"sass",
"less",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"svelte",
"php",
"xml",
"xsl",
},
config = function()
-- Enable snippet support
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true
-- Set up emmet-ls using the new method
vim.api.nvim_create_autocmd("FileType", {
pattern = {
"html",
"htm",
"css",
"scss",
"sass",
"less",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"svelte",
"php",
"xml",
"xsl",
},
callback = function(args)
vim.lsp.start({
name = "emmet-ls",
cmd = { "emmet-ls", "--stdio" },
root_dir = vim.fs.dirname(vim.fs.find({ ".git" }, { upward = true })[1]),
capabilities = capabilities,
filetypes = {
"html",
"htm",
"css",
"scss",
"sass",
"less",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"svelte",
"php",
"xml",
"xsl",
},
})
end,
})
end,
}

View File

@@ -0,0 +1,17 @@
-- noice.lua: Replaces default UI messages with polished, keyboard-friendly notifications.
return { -- lazy.nvim
{
"folke/noice.nvim",
event = "VeryLazy",
opts = {},
dependencies = {
-- if you lazy-load any plugin below, make sure to add proper `module="..."` entries
"MunifTanjim/nui.nvim",
-- OPTIONAL:
-- `nvim-notify` is only needed, if you want to use the notification view.
-- If not available, we use `mini` as the fallback
"rcarriga/nvim-notify",
},
},
}

View File

@@ -0,0 +1,228 @@
-- nvim-surround.lua: Quickly add, delete, or change surrounding delimiters (e.g., brackets).
-- nvim-surround configuration with autopairs, treesitter, and comment integration
return {
-- nvim-surround: manipulate surroundings
{
"kylechui/nvim-surround",
version = "*",
event = "VeryLazy",
dependencies = {
"nvim-treesitter/nvim-treesitter",
"nvim-treesitter/nvim-treesitter-textobjects",
},
config = function()
require("nvim-surround").setup({
keymaps = {
insert = "<C-g>s",
insert_line = "<C-g>S",
normal = "ys",
normal_cur = "yss",
normal_line = "yS",
normal_cur_line = "ySS",
visual = "S",
visual_line = "gS",
delete = "ds",
change = "cs",
change_line = "cS",
},
-- Custom surrounds
surrounds = {
-- Function call surround
["f"] = {
add = function()
local result = require("nvim-surround.config").get_input("Enter function name: ")
if result then
return { { result .. "(" }, { ")" } }
end
end,
find = function()
return require("nvim-surround.config").get_selection({ motion = "a(" })
end,
delete = "^([^(]+%()().-(%))()$",
change = {
target = "^([^(]+%()().-(%))()$",
replacement = function()
local result = require("nvim-surround.config").get_input("Enter function name: ")
if result then
return { { result .. "(" }, { ")" } }
end
end,
},
},
-- Markdown code block
["c"] = {
add = function()
local lang = require("nvim-surround.config").get_input("Enter language: ")
return { { "```" .. (lang or "") }, { "```" } }
end,
},
-- HTML/JSX tag with class
["C"] = {
add = function()
local class = require("nvim-surround.config").get_input("Enter class name: ")
if class then
return { { '<div class="' .. class .. '">' }, { "</div>" } }
end
end,
},
-- Template literal (JavaScript/TypeScript)
["`"] = {
add = { "`", "`" },
find = "`.-`",
delete = "^(`)().-(`)()$",
},
},
-- Use treesitter for better text object detection
move_cursor = "begin",
indent_lines = function(start, stop)
local b = vim.bo
-- Only indent if the buffer is not a special filetype
if b.ft ~= "help" and b.ft ~= "man" and b.ft ~= "qf" then
vim.cmd(string.format("silent normal! %dG=%dG", start, stop))
end
end,
})
end,
},
-- nvim-autopairs: auto-close brackets with integration
{
"windwp/nvim-autopairs",
event = "InsertEnter",
dependencies = { "hrsh7th/nvim-cmp" },
config = function()
local npairs = require("nvim-autopairs")
local Rule = require("nvim-autopairs.rule")
local cond = require("nvim-autopairs.conds")
npairs.setup({
check_ts = true, -- Use treesitter
ts_config = {
lua = { "string", "source" },
javascript = { "string", "template_string" },
java = false,
},
disable_filetype = { "TelescopePrompt", "spectre_panel" },
fast_wrap = {
map = "<M-e>",
chars = { "{", "[", "(", '"', "'" },
pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], "%s+", ""),
offset = 0,
end_key = "$",
keys = "qwertyuiopzxcvbnmasdfghjkl",
check_comma = true,
highlight = "PmenuSel",
highlight_grey = "LineNr",
},
enable_moveright = true,
enable_afterquote = true,
enable_check_bracket_line = true,
enable_bracket_in_quote = true,
enable_abbr = false,
break_undo = true,
map_cr = true,
map_bs = true,
map_c_h = false,
map_c_w = false,
})
-- Add spacing inside pairs
local brackets = { { "(", ")" }, { "[", "]" }, { "{", "}" } }
npairs.add_rules({
Rule(" ", " "):with_pair(function(opts)
local pair = opts.line:sub(opts.col - 1, opts.col)
return vim.tbl_contains({
brackets[1][1] .. brackets[1][2],
brackets[2][1] .. brackets[2][2],
brackets[3][1] .. brackets[3][2],
}, pair)
end),
})
for _, bracket in pairs(brackets) do
npairs.add_rules({
Rule(bracket[1] .. " ", " " .. bracket[2])
:with_pair(function()
return false
end)
:with_move(function(opts)
return opts.prev_char:match(".%" .. bracket[2]) ~= nil
end)
:use_key(bracket[2]),
})
end
-- Add arrow function for JavaScript/TypeScript
npairs.add_rules({
Rule("%(.*%)%s*=>$", " { }", { "typescript", "typescriptreact", "javascript", "javascriptreact" })
:use_regex(true)
:set_end_pair_length(2),
})
-- Integration with nvim-cmp
local cmp_autopairs = require("nvim-autopairs.completion.cmp")
local cmp = require("cmp")
cmp.event:on("confirm_done", cmp_autopairs.on_confirm_done())
end,
},
-- Comment.nvim: smart commenting with treesitter
{
"numToStr/Comment.nvim",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"nvim-treesitter/nvim-treesitter",
"JoosepAlviste/nvim-ts-context-commentstring", -- For better JSX/TSX comments
},
config = function()
require("Comment").setup({
-- Use treesitter for comment detection
pre_hook = require("ts_context_commentstring.integrations.comment_nvim").create_pre_hook(),
padding = true,
sticky = true,
ignore = "^$", -- Ignore empty lines
-- LHS of toggle mappings in NORMAL mode
toggler = {
line = "gcc", -- Line-comment toggle
block = "gbc", -- Block-comment toggle
},
-- LHS of operator-pending mappings in NORMAL and VISUAL mode
opleader = {
line = "gc", -- Line-comment keymap
block = "gb", -- Block-comment keymap
},
-- LHS of extra mappings
extra = {
above = "gcO", -- Add comment on the line above
below = "gco", -- Add comment on the line below
eol = "gcA", -- Add comment at the end of line
},
mappings = {
basic = true,
extra = true,
},
})
end,
},
-- nvim-ts-context-commentstring: JSX/TSX comment support
{
"JoosepAlviste/nvim-ts-context-commentstring",
lazy = true,
opts = {
enable_autocmd = false, -- Disable auto commands, Comment.nvim handles it
},
},
}

View File

@@ -0,0 +1,25 @@
-- treesitter.lua: Enables syntax highlighting, code folding, and structural analysis.
return {
'nvim-treesitter/nvim-treesitter',
version = false, -- always pull latest
build = ':TSUpdate',
---@type TSConfig
opts = {
modules = {
highlight = { enable = true },
indent = { enable = true },
},
-- Parsers that should always be present
ensure_installed = {
'bash', 'html', 'java', 'latex', 'lua', 'markdown', 'markdown_inline',
'python', 'r', 'sql', 'vim', 'yaml',
},
sync_install = false, -- install in background
auto_install = true, -- fetch parser when you open a file
ignore_install = {}, -- none
},
config = function(_, opts)
require('nvim-treesitter.configs').setup(opts)
end,
}

View File

@@ -0,0 +1,40 @@
-- ~/.config/nvim/lua/plugins/nvim-ufo.lua
return {
"kevinhwang91/nvim-ufo",
dependencies = "kevinhwang91/promise-async",
event = { "BufReadPost", "BufNewFile" },
keys = {
{
"zR",
function()
require("ufo").openAllFolds()
end,
desc = "Ufo: open all folds",
},
{
"zM",
function()
require("ufo").closeAllFolds()
end,
desc = "Ufo: close all folds",
},
{
"zr",
function()
require("ufo").openFoldsExceptKinds()
end,
desc = "Ufo: open outer folds",
},
},
config = function()
require("ufo").setup({
provider_selector = function(_, filetype)
-- File-types that constantly throw UfoFallbackException → skip TS
local noisy = { "html", "css", "json", "yaml", "txt", "md" }
if vim.tbl_contains(noisy, filetype) then
return { "lsp", "indent" } -- 2 providers max → no warning
end
end,
})
end,
}

View File

@@ -0,0 +1,88 @@
-- vimtex.lua: Full LaTeX editing support (compiling, viewing, and syntax).
return {
"lervag/vimtex",
ft = "tex",
config = function()
-- PDF viewer
vim.g.vimtex_view_method = "zathura"
-- Compiler settings
vim.g.vimtex_compiler_latexmk = {
continuous = 1,
callback = 1,
options = {
"-pdf",
"-shell-escape",
"-verbose",
"-file-line-error",
"-synctex=1",
"-interaction=nonstopmode",
},
}
-- Concealment (makes LaTeX prettier to read)
vim.g.vimtex_syntax_conceal = {
accents = 1,
math_symbols = 1,
math_fracs = 1,
math_super_sub = 1,
}
vim.g.vimtex_toc_config = {
name = "TOC",
layers = { "content", "todo", "include" },
split_width = 25,
todo_sorted = 0,
show_help = 1,
show_numbers = 1,
}
-- Don't open quickfix window automatically
vim.g.vimtex_quickfix_mode = 0
-- kenable folding
vim.g.vimtex_fold_enabled = 1
-- What to fold (customize as needed)
vim.g.vimtex_fold_types = {
envs = {
enabled = 1,
whitelist = {}, -- Empty = all environments
blacklist = {},
},
sections = {
enabled = 1,
sections = {
"%(add)?part",
"%(chapter|%(sub)*section|%(sub)?paragraph)",
"appendix",
},
},
items = {
enabled = 0, -- Don't fold individual list items
},
}
-- Disable some features for better performance (optional)
vim.g.vimtex_indent_enabled = 1
vim.g.vimtex_syntax_enabled = 1
vim.g.vimtex_syntax_conceal = {
accents = 1,
ligatures = 1,
cites = 1,
fancy = 1,
spacing = 1,
greek = 1,
math_bounds = 1,
math_delimiters = 1,
math_fracs = 1,
math_super_sub = 1,
math_symbols = 1,
sections = 0,
styles = 1,
}
vim.opt.conceallevel = 2
end,
}

View File

@@ -1,3 +1,5 @@
-- autopairs.lua: Auto-inserts matching pairs for brackets, quotes, and delimiters during coding.
return {
{
"windwp/nvim-autopairs",
@@ -43,10 +45,8 @@ return {
-- Press % => %% only while inside a comment or string
autopairs.add_rules({
require("nvim-autopairs.rule")("%", "%", "lua")
:with_pair(ts_conds.is_ts_node({ "string", "comment" })),
require("nvim-autopairs.rule")("$", "$", "lua")
:with_pair(ts_conds.is_not_ts_node({ "function" })),
require("nvim-autopairs.rule")("%", "%", "lua"):with_pair(ts_conds.is_ts_node({ "string", "comment" })),
require("nvim-autopairs.rule")("$", "$", "lua"):with_pair(ts_conds.is_not_ts_node({ "function" })),
})
-- Add spaces between parentheses

View File

@@ -1,4 +1,4 @@
-- This is a VS Code like winbar that uses nvim-navic in order to get LSP context from your language server.
-- barbcue.lua: Provides a minimal status line with contextual code structure (e.g., current function/method) using Treesitter and nvim-navic for lightweight navigation.
return {
{
@@ -14,39 +14,39 @@ return {
require("barbecue").setup({
-- Create autocmd to attach to LSP
attach_navic = true,
-- Create user commands
create_autocmd = true,
-- Whether to show/use file icons
show_modified = false,
-- Whether to show file path
show_dirname = true,
-- Whether to show basename
show_basename = true,
-- Filetypes to exclude
exclude_filetypes = { "netrw", "toggleterm", "alpha", "NvimTree" },
-- Modifiers to apply to dirname
modifiers = {
dirname = ":~:.",
basename = "",
},
-- Whether to display path to file
show_navic = true,
-- Custom section
custom_section = function()
return " "
end,
-- Theme configuration
theme = "auto", -- 'auto', 'tokyonight', 'catppuccin', etc.
-- Symbols for different node kinds
kinds = {
File = "",

Some files were not shown because too many files have changed in this diff Show More