Compare commits

..

No commits in common. "3b875f57d2b5fb3fb839eea7a95c1e19be0dbeb8" and "fba7eb68ea70f2fec3bd1a4ae06d68fbe365e682" have entirely different histories.

34 changed files with 672 additions and 17719 deletions

View file

@ -1,7 +1,7 @@
# Статус инфраструктуры
> Обновляй этот файл при каждом значимом изменении.
> Последнее обновление: 2026-03-27
> Последнее обновление: 2026-03-23
---
@ -9,10 +9,8 @@
| Сервер | IP | Роль | Состояние |
|--------|----|------|-----------|
| **main** | 87.249.49.32 | Все продуктовые сервисы + мониторинг | ✅ Работает |
| **tools** | 85.193.83.9 | Вспомогательные сервисы (пусто, ожидает мониторинг) | ✅ Работает |
> mon (188.225.79.34) — планируется к отключению.
| **main** | 87.249.49.32 | Основные сервисы + мониторинг | ✅ Работает |
| **tools** | 85.193.83.9 | Wiki + автоматизация + почта | ✅ Работает |
---
@ -22,91 +20,95 @@
| Сервис | Домен | Статус | Заметки |
|--------|-------|--------|---------|
| Traefik | — | ✅ | Реверс-прокси, TLS wildcard `*.walava.io` через Cloudflare DNS-01 |
| Vaultwarden | vault.walava.io | ✅ | Менеджер паролей |
| Forgejo | git.walava.io | ✅ | Git-сервер, SSH на порту 2222 |
| Traefik | — | ✅ | Реверс-прокси, TLS wildcard `*.csrx.ru` через Cloudflare DNS-01 |
| Vaultwarden | vault.csrx.ru | ✅ | Менеджер паролей |
| Forgejo | git.csrx.ru | ✅ | Git-сервер, SSH на порту 2222 |
| Forgejo Actions | — | ✅ | CI/CD runner, деплой через push в master |
| Plane | plane.walava.io | ✅ | Управление проектами |
| Outline Wiki | wiki.walava.io | 🔄 Переезд (CI в процессе) | SMTP: Resend через walava.io |
| n8n | auto.walava.io | 🔄 Переезд (CI в процессе) | Workflow автоматизация |
| outline-mcp | — | 🔄 Переезд | MCP сервер для Claude |
| discord-bot | — | ✅ | Деплой-нотификации в Discord |
| walava-web | walava.io | ✅ | Лендинг (заглушка) |
| Grafana | dash.walava.io | ✅ | Дашборды мониторинга |
| Plane | plane.csrx.ru | ✅ | Управление проектами |
| Grafana | dash.csrx.ru | ✅ | Дашборды мониторинга |
| Prometheus | — | ✅ | Сбор метрик, 30 дней хранения |
| Loki + Promtail | — | ✅ | Сбор логов |
| AlertManager | — | ✅ | Алерты в Telegram |
| CrowdSec | — | ✅ | IDS, банит злоумышленников |
| Uptime Kuma | status.walava.io | ✅ | Публичная страница статуса |
| Бэкап | — | ✅ | Каждые 6 часов → S3 `walava-backup/data/`, 7 дней |
| Authelia | auth.csrx.ru | ✅ | 2FA SSO, защищает traefik dashboard и plane/god-mode |
| Uptime Kuma | status.csrx.ru | ✅ | Публичная страница статуса |
| Бэкап | — | ✅ | Каждые 6 часов (00/06/12/18) → S3 `visual-backup/data/`, 7 дней |
### Tools-сервер (tools, 85.193.83.9)
Outline и n8n **переехали на main**. Сервер ожидает переноса мониторинга.
| Сервис | Домен | Статус | Заметки |
|--------|-------|--------|---------|
| Outline | wiki.csrx.ru | ✅ | Wiki, аутентификация через email magic link |
| n8n | n8n.csrx.ru | ✅ | Автоматизация workflow |
| docker-mailserver | mx.csrx.ru | ✅ | Postfix + Dovecot, порты 25/465/587/993, DKIM+SPF+DMARC |
| SnappyMail | mail.csrx.ru | ✅ | Веб-клиент почты |
---
### Почта (@csrx.ru)
## Почта
**Аккаунты:**
Используется **Resend** (resend.com) для исходящей почты.
- Домен `walava.io` верифицирован в Resend
- Отправитель: `noreply@walava.io`
- Outline шлёт magic link напрямую через `smtp.resend.com:587`
- API ключ: в vault как `vault_resend_api_key`
| Аккаунт | Назначение |
|---------|-----------|
| noreply@csrx.ru | Системные письма (Outline magic link) |
| admin@csrx.ru | Администратор |
| jack@csrx.ru | Личный |
**Входящая почта не настроена** (нет MX, не нужна).
**Архитектура почты:**
```
Входящая: internet → port 25 → mx.csrx.ru (85.193.83.9) → Postfix → Dovecot → IMAP
Исходящая: Outlook/SnappyMail → port 587 (STARTTLS) → mx.csrx.ru → Postfix → internet
Outline: outline контейнер → mailserver:587 (Docker mail-internal сеть) → noreply@
```
---
**Настройки почтового клиента:**
- IMAP: `mx.csrx.ru` порт 993 (SSL/TLS)
- SMTP: `mx.csrx.ru` порт 587 (STARTTLS) или 465 (SSL/TLS)
- Веб-клиент: https://mail.csrx.ru (SnappyMail)
## S3 (Timeweb Object Storage)
**DNS-записи для почты** (все должны быть в Cloudflare):
```
A mx → 85.193.83.9 DNS-only (НЕ proxied!)
A mail → 87.249.49.32 Proxied (webmail через Traefik)
MX @ 10 → mx.csrx.ru.
TXT @ → "v=spf1 mx -all"
TXT _dmarc → "v=DMARC1; p=quarantine; rua=mailto:admin@csrx.ru; ..."
TXT mail._domainkey → "v=DKIM1; k=rsa; p=<KEY>" (генерируется при деплое)
CNAME autoconfig → mx.csrx.ru. для Thunderbird autodiscover
CNAME autodiscover → mx.csrx.ru. для Outlook autodiscover
```
| Bucket | Назначение |
|--------|-----------|
| `walava-backup` | Бэкапы (каждые 6 часов, 7 дней хранения) |
| `walava-outline` | Файлы Outline (вложения, изображения) |
**rDNS (PTR-запись)** — настроить в панели Timeweb:
`85.193.83.9 → mx.csrx.ru` (критично для доставки в Gmail/Yandex!)
**Автообновление TLS-сертификата:**
- certbot renew cron: каждый день в 03:15 и 15:15
- deploy-hook: после обновления автоматически перезагружает Postfix+Dovecot
---
## CI/CD
- Репозиторий: `git.walava.io/jack/infra`
- Репозиторий: `git.csrx.ru/jack/infra`
- Триггер: push в `master` запускает `ansible-playbook playbooks/deploy.yml` + `playbooks/tools.yml`
- Runner: `act_runner` на main-сервере
- Runner: `act_runner` на основном сервере
- **Правило**: все изменения только через git, никаких ручных правок на сервере
---
## Бэкап (что входит)
| Данные | Метод |
|--------|-------|
| Forgejo DB | pg_dump → gzip |
| Forgejo data | tar volume |
| Plane DB | pg_dump → gzip |
| Plane MinIO | tar volume |
| Outline DB | pg_dump → gzip |
| n8n workflows | tar volume |
| Vaultwarden | tar volume |
| Uptime Kuma | tar volume |
| Traefik acme.json | в составе volumes |
Расписание: 00:00, 06:00, 12:00, 18:00 UTC. Хранение: 7 дней.
---
## Известные проблемы / TODO
## Известные проблемы
| Проблема | Статус |
|----------|--------|
| Outline + n8n переезд на main | 🔄 CI задеплоен, ожидаем старт контейнеров |
| Authelia всё ещё запущена | ⚠️ Нужно удалить после деплоя (remove_orphans уберёт) |
| Мониторинг переезд на tools | ⏳ Следующий шаг |
| Отключить mon-сервер | ⏳ После переноса мониторинга |
| PTR-запись 85.193.83.9 → mx.csrx.ru | ⏳ Настроена в Timeweb, обновляется 324 ч |
| Tools-сервер не бэкапится | ⚠️ outline-db, n8n, mailserver/config не входят в бэкап |
| Tools-сервер вне мониторинга | ⚠️ Prometheus не скрейпит tools-сервер |
| SnappyMail домен csrx.ru не настроен | ⚠️ Нужно в админке: IMAP mailserver:993, SMTP mailserver:587 |
---
## Сети Docker (main)
## Сети Docker
### Основной сервер
- `proxy` — публичная, только для Traefik (нужна для ACME)
- `backend` — internal, Traefik ↔ сервисы
- `forgejo-db` — internal, Forgejo ↔ PostgreSQL
@ -114,5 +116,11 @@ Outline и n8n **переехали на main**. Сервер ожидает п
- `plane-internal` — internal, все компоненты Plane
- `runner-jobs` — публичная, для job-контейнеров CI/CD
- `monitoring` — internal, стек мониторинга
- `authelia-internal` — internal, Authelia ↔ Redis
### Tools-сервер
- `front` — публичная, для port binding хоста
- `outline-internal` — internal, Outline ↔ DB ↔ Redis
- `n8n-internal` — internal, n8n изоляция
- `mail-internal` — internal, Outline → mailserver (SMTP без auth)
- `webmail-internal` — internal, SnappyMail изоляция

View file

@ -48,7 +48,6 @@ discord_bot_token: "{{ vault_discord_bot_token }}"
discord_bot_app_id: "{{ vault_discord_bot_app_id }}"
discord_bot_public_key: "{{ vault_discord_bot_public_key }}"
outline_mcp_api_key: "{{ vault_outline_mcp_api_key }}"
timeweb_token: "{{ vault_timeweb_token }}"
# Server IPs (used for cross-server Traefik routing)
ip_main: "87.249.49.32"

View file

@ -1,183 +1,146 @@
$ANSIBLE_VAULT;1.1;AES256
61333332643965336563306337363339366563626363333837343831316137346335343031653735
3139333134386564376566616637613761386663346337320a613937373264653435636334633432
63373735386239383034343935336235646432316364366361323664393062383233393336343434
3238623361636136310a666262363065346234636630316264613662336261336234356135333662
37313233396265313961316135376334356662303161643132623734303864313765303264313934
62636265356161343635626337396466623766323839336266353063316333336534313138663336
61303834306233363739326237376339616638323162333631346565366232343966663037396161
39373437353732393765306230373864356135666630613930306335343466643435326630373831
64663363646334306339363265363162323561333531343932303034633331316262356533666363
63303431386130386536616436393764666136323636643431336565623537393638646632363762
34383731336163656236303937346466323332616433613938663134363661323538363963393533
38646338303464303765623433376163663232653931353034346633656336346530663864396465
31313864333730356434343066636331663838343635633235623162393963366436336437396363
62393761386636366664316436323535343032356537363833333962303761663563623833396638
66343033306237376437633238656634346264613561336335306432353635313363356333303561
35663839366434303366376266663033616364306636643437663464343866336466356133366135
63646233303037663563616661316533366264366535636333316333633437333036613039626636
38356363303761643630653964616136303566626363343436633834346230356430363132643637
32633761333564616639383463613662373163386264353935396332623065633366306330386266
39356665666530333331613632303461663764356638303234623031623731613431636666343366
30396132323536363838653730656263366165633964666463316236346562666639323339313333
61613962646232356264643932663962366266373332333938366230636232353630396532373161
34633230386162626535633032353330613337626237623937646165636132666630323038623233
35363032643936306230376165613632633061353430386366636238653034313038356366323064
39643961623136343130646666646630346638653136326365386437336466636263333238666266
39373861376261393365373238623662626137313862326333333135343730376439336434363633
34303230373933346563656363613034393535646539356562353635663065383137643337386564
62376230373334626239313661633835333062656432633931333735653736636238663331613165
34343538663264353536656661643265383865333665656363326166326239666136646435616130
33343730386232336232363561363439383666313762613933666235363665356264386161656631
30386331663232653537653036653531346265386239326134323066316139656434356232303938
66633562313037363066363332353262623831326263316339376436333833313231363662336432
32333961616231303162383233666562636164633735326338323363323965323436623537366232
30643533383733333532383766366265363636366136653332666634336263306563643638373739
32313937303832353236656437626662306132323566393763643665633736376436356235616332
62653838356162323066613565323835313263366134666132393136306264306337353834646334
38346166383539383864386362393932353164636239383366383664636632313534353866643663
63613338333466653365663262313561313763323035316638376666643035616663646664313061
64663535376432656439636534653431373133616239373161653134303039373163633866303233
31363539643264323434393535353233303435626230303636646364646331356266396239373438
38376638383339626139363863646565316433373634633132616633383438663534306665373166
34633935373430653666666238633634323132313664373562653931313131633564363437626161
61346263386235626465316561393064646333326165633338393037363732323936336439363564
32366532663539316638366439646634313665623365343566623232376161346439623433336665
34613964303366396266373835356333393433343461633765663537366261313965626133346638
64326131616439306532366162616239343234616563393436343062373933313762316236386666
30626162376335313738663566386138323564326134623563663134326662626362393362666236
38643237323062363433303833616533383437636262663762363266383962333936623032373334
34323138613463623537643262663662356534313031666564383761326133613530363665613933
62393432346231373063366238326361306161306235393333366532306263333636616536353363
30646165613738656332363337383031326539393462393365613766346438323539636535366137
36383334383038343539666562343137623030663935663639303333643933633566373264326330
38313037346635663430643238323863363831393064346436636463383738643832336362373537
63333064396433666666653935306339613062373539386665386264623462653535303933396263
34336337626462353265336633346439316330303639323563613561346532376530376530666238
63626436326339336436303232666665623632343433306362306661366565306436613765656539
63373764313835353437653535333935626136613563396662336162623237613431623463663561
34643936393030303938386339306435326561663062316539623661323861303330653765613065
63323532666139336631343839316638616132636366353438366566396664393561333330323861
31383236316336643238653132326362343235343534633032376564356463633539396331646330
61663232616534626130613331396161373538396430383963346262623230326638396338376631
32343630353734656163343931396238653731316562356433656335636339616231323763633033
65666539386134646561366264376333656361386362636330363434343865383336313535383736
32633465663230356566303134633131383565366163653333613433633035616565393733306466
33626534383436366162633831306231663361363836363034393462303861373862626165326163
34656233636161336534633562333263346639636133663235626436393966666438653739386436
39336364313238303737643762356530333537623565663361646233663336376533623233373738
36336164343935633533376463373437663830303965383134383431303463376265316663663862
38663964653133616464646231353435353863613131633139393331356634643334656133623834
33656230343465343035656532633939336339353934623466663537363163626463633737303738
62346563313061343661356564326565363531353464653135353763333038326333313434306132
63656535623064396530663932656434666661663763666535613661333038306362653031363533
65373234386130313634613934653836353434363432653463376464643935643836376237636236
65323261373334613630343865393039396262336230663030323335363730646361633363613131
32363664613862653035383436666161363366396161313437643662363635613532373765666237
30303261333363373663616535343436306530346666323036326365616334383134383435306637
32653935363962393363306137373861383461346439633030323935636564623264303638353539
32366363616636383337313763393765316637656638356365373765306639336332363631383734
39393131393765383165326662633438366330633632343665653735313032643037653430363761
36646365623835343466313764636236623936666330643730386238353565646565656664623861
36333438306161666232613839356131306166376434336431323036383634383539306134323038
32333666663237643865643461373538626264656335373534303236616435303039346661633338
66363238386233303532633431666263323235613335626361326461353466613661616433613736
38636331663635396636303931353338333437393936373631366130366632313037363262613333
30303261633263306661633862326665313338326433373033333639656663393564343536373063
64336464353937393238346331313166326238303033663838306339303463643364333330396435
36313139636338323233353262373864643463386464343037333733343236303132366236353231
62316533613734313038633461323864303862616339356236313030623963376432376365366166
34303564333863326265373066346233363964633061626335623636373839633366336266343161
33323866353937653835396534346235646236326563386234386465333839333464653462633264
62396163353039613436363866623938326164323638346531653533326239636562353334366564
34353235303838353765333636626261383462373539646230303332653364346635616232313131
33636636616439666134383536383965363532333635663232323063623031666562653461646132
66353530373466363731633435653732663337373434323436306539376632376538373636613561
62393631343639623736306336376237396239363232303732653434666366396162346463373864
34386435663438353031376639613530356437633665653266336436616539333262646362623836
65646537663031616466303065646137633333623932666534326139373064633834393666366661
32343065386362343331383637333766313330383532326132373338386435353463616233323633
65666362663638663938643133333631636539623864653864366263303830366230306537663162
63396236636634616238353863323332626565393139663537636435646132323864303539316364
36353034333664316338383131316335333133303930633030376238616164633264303662653363
62376361376637373466666534663136363639383262343235366366303530343361663764333066
34346130396365386232313365646138353733376430373066323561363235633236636533303433
33623839343532326163663865313332366663643861323737616362636233343963613934383437
63336565323935303337336434303561356566616537666330323039346531623039656162636538
36353232346633653135393266313865663532373834613663386261666535363965653366643666
38626563353765356435653534356630663730313932323739623866373335333338646434326539
34323561313237633935643364646162633834616466653138373639326464393837666630386236
65653066346361616131626238616564303033623966303230343638353464373036333863373465
63313562386237623532323432663562333862626262656630303032656565613635313034303566
38656563393934663364366333316431653563643963383838396366333338323236343164626363
66323131363361643031343366393034373131623631643265383532633864623362613566316565
39373538626364613163333836376666643764386330616564376364626362373962623838613533
64386438666365656163373065653832326132343535353238363165663461623264343831343037
35323433613832643934343633376136663733653465653762313963636535373539356565383937
66383538643435643461616337333864336166663030353762316630613536656466303761353531
35666539376633383236396535336335303764663435373633373335383863376466313633393830
37323638633437373966343935623536353161316366623431376565306666616662316561316130
36336239663232316262363462343431323664373330643361656464343938646330373631623565
33306365653037656232326461343336623134643433666561346164626236383565653061383961
34363734633237643064376337336163623035316630646631666564653634323339656164363063
30363165643663613138623537363234313939653461353130366366336662663236323131303239
35316462316132633330366634366533336563623730633664316564393263633436306463353639
32383031386638313533613665623966363630636637656164363736353937303162373839623339
35393834393235363966613032643935663139653733316261366566623566396565636636663136
30306433386533363038633666383862343064313338363835623030316466653564366562393733
32343761623234353665313061353330623666333037393633316333303436373530653666646632
33393637363333643638333666623430623963373963303739653261356461393933646533623035
63343936616233353035626565633439376339386237396433663933633335316131333834623264
35353866336564623166323236663733333532333465633661663666316664626239653631643232
32393135316533393464636461636163373762363638333439376335643237383262663131383032
36656134666130633237383733333532646365323131626430653031326363626438336436656135
66303430373230386166396132316530316536646165666633386164313061376439653663326363
35376634343735393735396365366239643865356231633530313865643438633934656664356366
61306136623635663165366637376565366461383363656365623136353533663963623766376334
63363661303262353939653366383931623235643632663035353431356434396230623366666439
39623265343763303030366133323263356261383361646662386565363238306437653732616232
32303461373239353737666638313130373765626462376137363162386430333762623663663934
62383331396331323961303461623130663931303537353831303664306564643866623739356566
65643631643739313039333337646162653065366462383938303163636132356263386461323535
62626436336464333961633535333164363532616163353337633430653063626336386635633331
30313862356165613833316662613764316139363335633833303864656434383062616434643864
66646233333937393462336236643161663135313163613664376665633235306233353561313435
30383363613330306131303262646130633032386531333565643833313566636231323130626135
30383831306136373537346532383866326638663263333737643737363535653037653163346536
35663331356330313665643337383639366535616333663233356165366363626530346538623636
62393261333938333031396138303965303762353838333038643864626439633935393538633164
34353333393535363939616366376335353435366161313563396632333665363538323962366362
64353631393730303162653131306130376131323665626339636130613137343665303036383862
31383066353238396131336538656133626335636635643366313838323635323537323638303162
33353466383738353637623263633564383236633434373939313936393638303862623536363035
63656232376562373963633865346536373532636334656238356131656435343333386233363132
61636433613866373664363138323764356538343363613232626539366237666263333165383566
63663033336266663266333335636264666431306435636133363331623364336432616166346130
64303437316633306663633933646262653336666263313738326465616366666537643662383235
32633534346233303336623565323937356538366532313537363161393030653534323033316535
63646335623535623935383231373438313936333339326263353861623332336239653765636333
31333865326164353937363863353865626531373337396639353339653732356161333733663961
64313031306530663234313630613463383236373331356230616433623562346266356162373034
30363832646538383138393836303331323562633737386132393739373832326432323831616565
64303937653130643766663132306662313639333962323564613935336231323564333236636330
32633937656137343939653964306633326237313736633535393338346330383363383465313937
66613965326630356131333334393865646132633366623335653733393462333166643537393762
65656234636334653363656438633361313039323239353031316333323037636364636438626562
32363834616630373738323064633363356536336336343138373761643935356339623761383138
31633638323934343965346539353232383736663738373130626264383631343463383563373430
35373736313663346662356564636331396464353735643262373362313261306661363136663239
32313036356562333465613366386439323266613437653539313364356631366365633439356261
32316465386266373435383632653564623166656330393637393830336462383561353032376535
34326165626639646364396565363365643131666533353433643132386333613863383066323632
63656566383161303038346138383835303430353161656662313336373536653236356461356664
64386532343061616136343536613064323236613532356237386239373938366636323438373732
62326430646539613363376530626331303636393865353438666434376561666539343737623439
33653962353061666335653663643861636564363761346332326436653339613132393730646535
35353630616637653731396438653635306663626363346565306662623237666531333338333263
63663232633437386434356631373864366234653965336338383965363334623833333437383937
35383864336431656136666630643564316163393037636161303339653233393866616531623437
39643666626166353831616137623530323261373465323436613535313362616131633266373332
62363163316335626461653434643532666630643936666139643634393765366234356633333534
36376330333461326263343164323031366232623264353930336561643637323230336537363236
65353136363932623930376234653732323562663239393337336538383131616564616133613834
3665613863646666663431363062396330366438666431393536
64373338333466646131363639303563393130303436613766383337383937336663643765636563
6438303632663737356166633739383065363535653234380a316266613236313532353361356339
39343137333435333734623665393033383136383932306634323563623135626235633430333232
3166623031613436650a356538646633343266323533653466623961363231303730333161313332
62353133336462636564643835613361303039343737333938343162336566303737336534376364
65646236633934623937393963653166356331393137333730306663333133326631393163386361
62363065653232623634383032376165366330343533386266656334623334623333366332313065
36666133383535383537663339656162616638396464653238313732646435376537626335663563
64656237353263376466303632343432613065373061353632346539353562623064616534656466
34663534303838396165333761313337393261386433376631313066393765613232633535313863
31643535653431646564613232323538363061343664396332373565303531383438383665656233
31383731323633353635373232613036633433376636386537353136396336373663326635353838
36396234396436636565646638303761633061653761366238656266323533303466343666326365
64336164326566646630643361613264636533373330646630386266663965353934633765326434
64646162306365303065303830613636346232333964633035373463333630386132396632313035
62623139393430633665333034663661313965663134373534623166333435343132346662393361
32643332623731386535373366633563333961326632356265633330313839616463313834636164
37626330313465316364313334623031383531393132303563303263646637383838623863633566
37383036623030653630343235393336616435346231363338386334653732633634353565396638
61306565623166333332376439636636333133333934363631613166366664303362636362326534
37323561313538343466666161313937353630396162333361343437383537363966383730373364
61363733646237623761386436653165616136633131316538323266666262373761663066653934
63313266396232383662663735333530393633616637653466383734326636663137336462383134
35353536353763616531623433653764623535623464363432613635663137363738323231353939
64653363396634613737376462343139316337366234653639386335656462643661353764646363
62636537326639373665616134363837633237613734383761346662363931346634323161383364
36613236646163396636383036323566373664393963623961366634333337353833643439313565
32313631333433613139313533306436346334383239366337303865336562313235643734366332
30323530643034303336336531363433626431346464303562396366333336386561313964373364
35333037353030383861663165366534396637343634653239653732663138346566653035386135
34333732336263643532333133323063613363393037313639663966373938393762316663333066
35393463613232303338386535333935646466623162623531653663666235383263383461656462
39663162373537653735653536396164366330616164613561663363323463313634626632383964
37303134663764643163653062366537383630376463333363313839366134313535653866626332
63386435653065306231616532616563336330333235303562393731613434366438643038623234
66313038623137306666643762393234356332333532316333353266346163303036393366383238
35383232636164633132643339623936633732663966613961613964333631653433343731333962
64656239303631386461663036393039326330393435613562356263363461363261366430393736
39336536393834636336643837663038623863373362306364393166326135343430356437313435
63643064343438373661306363306233623563363061613032386165303262303533363433383062
39653166646534633733333335356464646339323961653038383165363663313738643363656663
34633664343833656332643663323036303665616265633463323462363330303333363836306331
62613937363936633533633965323530373761376438303061643063316162336566613934393562
65653463376233353662336536303731613632623836623263616435353235656232313438663236
64663265623431333831383866316237663237333235363733623739343134306636366366613737
62616430666233646131353463396236316366613430663231643435653161366562353265356632
65613164376437353566643435623366323266336635666336373465303936646665656135346135
38663938633063346164623962383733333239623565336539663531646330366536363336643032
30376562663364303864376330326638336131656362313264346361343236386664376338333963
34356236346534336233626361386231613761386135303335303233303730323939383735643138
63393433363734346436366436633064643532613335613062306231343163393962343031613062
30303962666434616235613062376339636433636466633935303837343937613239663161353038
61663130396262636339316161313636636339343033303132373733336663643433613161653361
31316237653461336335626138646139376264643733336434326339623330373337333761656333
65353837323563623561663865663664356534626433373934393263353234626630383738656362
31333338663363366531646230393663646462613164326366356238373137363230343561623934
65353963643336376231613130626466353735396166373165643965313464346562323362306161
62353830346130326261323963613938333136393665343566656636653939306466316432653431
66393466393164346237366365393039353563373861303134656662646364626562633538316531
62346161653038303836643932656563363230613732373662396133353736323138336162343936
34383334333031383537366533313161373937313261306563653431363961383563346565646333
66656433663538373038646266373538313365656532616634333861303139616236663765353532
32346638633434376232666635306331633363623639383464303434633936653162356264373766
31376431633261346230363938396437643938343637646564643966346261303932613563646166
61643263396163313735326239353561636333323765653734393132623062346630643730366235
35623535373039623933383131373032646330383764616565613364646431343631646430383538
30376535376261633738326238393634656433316234333432303439393137653362383139353466
64323965633337343161323165376531363066356432303832393065663639653363313531373436
35323130636161626565666632626633393834363136633337623839323939623464653330343963
33336161333865613634303334336436336561653338633439396335396635363832333064376637
30656665363434666439373033633462613136353339623730376238643435616532633563383764
39363666313732333030333338616532636463623335633366333235333961636136636436336534
37353063393763646339613938366132396565313833613066303664666165646439623832336432
34383762623735306233386566363230643063653635383636666639303637316131383163356133
66363530383734313136646633613166323761393531356537666231613339353066303332373462
63663233316631653234383234656262346238373762616138363130386664383133356239656161
38313638333231653066646165636231306239353766313437336634346664336330616465343430
62373661326237393666363737396630643034356666663338346664643837303331613961376634
64633761653436343135356364363362306563643536656437663836613766643763623334333361
65636366663339636438346631616239393865653138656262653632386238303566333762616634
61623931663466383736306563653231333234633963386333323939316439306461653064373662
65616638343930636636633230366131356536333236306339363562383063383035326635346335
65653462626538666364626635663331393263386630323235326334613830653432613334306461
37353232346337363034653633313565336565333934633062623136623062663262386331373862
32303730313034396337633132303531353436643662646564343635386163643538623935613661
32643836666438323535656332336339323333373634363664383866393765646430393364653563
33376634316137363066393337646336623065623636643862393534316630303562633761666639
65326233626435616531383939323035663333626134303631336532643136383938303839323639
34316261613761373934396335323763336663306264373431626134313935343062333930336133
39656161373532343934323263316666666430316462636439653762656236656635613531643731
33386265623531353335376636623564633234616465653166363830363531393933393163353033
62323237346263633032613161363831303963356432313534313832386138393335336166643436
30353762363434373836353966396265666166636561346436373934333339626637646662613261
39643664303034353364333538356536666163623538656261613265633839316163303732646637
38363465303362303566343165623364376532626137353237333165343162363537333237646230
35373464333365663163303634333439333938643334393136643437303064396631323331643662
62386534303630373236343730626562323738313561326339633061356534323862323033356539
39316162656435663133663533646331393636613037643866303534646166646662613664663561
62383035313831333736653831333739356535623864666165343362373933336366326264396331
63653837623433396636323265356165396437316538656533363064393033353061626463636533
39636463646233363233376365323731636433663765326232613335356234626635663538343061
39613837656661363439346662386563653361613435626163376232306635376537373931646637
64313537356431626466303165646538323234303065363163323431663962323030623263623233
34636662366266653538323337656662633938616437323862343064353533306437656136323939
34376137333135343333383633326465373164643636313239343365306237316238323534373239
33383434313033633337386438613134326430306536643666656534326538396166656265346531
30613434343334383135376337383034373365313762663131396234323330663565666431383264
63396430663733646337656164336630386164373964376439663465626165656632623635333766
36396439316538323530363266303366326230366564616639613738623463623835313264353561
32633065646161633462346634393737616333333566353630666565656431303162353633646138
62643634316263313034383063643438396537393361373632323739336262393639666537646266
34313466313461663664623430363634383236636330376165633430303665613261346631643938
39326632383565346663303937653138623433643038386131343435366361393137353062346562
62316337616435623762313630643966343836353163356534666363346239363638303031383231
33363830376337656537356635303636323037623763306136363761353037623137643832356562
33333937656239613562643661343634613230386130353439323139313965393266376565656338
31663438336538316663623939363633316363656661646162363365303065653766313161663334
33326236643039333862363034306432326634626330373862653761616238616435363233616337
36653532316362643366333566616664353938363032613766656235386536343737313231613334
64363661333664616337633565336137343361323131363034346437343265343634356139336263
33626261623864613039306335386536656636303238316265313863616134306239616661656133
32303036376431646238663337303737616232666130623730383166646265333062303263666463
65363164316634613231323065353331363035353335386662636334373930646437376239353531
36323361323661363739373665373838346138346662386235353136653230393939393332653638
38623866623461646330376233333837653334333665393665396261653065623835313831323936
36366438623563373937663233666433666363353132373333613734393330663133313966363162
31356364643830353630646263313162346664353736383236333235633838366633643636353032
38383633323438383433366632343433353633633537366231613537333938396438643732383530
37323665316435633961323230396265353930343537366666323034333235616431376138386536
65386432623530323935633938393161633430663063303837353565616561396434333063346462
35616366633438343330663232303366333662356364373964346439343961396561613963396238
37643430376130386366316533316561323466333061616266643962336331633761343935613337
30613235316130626165343232656165646536383531383430366365616466353939346638656561
38373638313864663862653634613631313361363161666135333532666430373764333233333938
62313330383337316566363531373236373834613166356538653037663137336131663138333039
62333634326434353736363662346530353939316464313364373135323765383232323135353065
63393163623937366135356233653866656236366133366530393864366165373436333235376165
62333763323237343133323538613832363938353431316566373966393365346337653638303139
39633337633733316465376161636134616637313733303663373766383664333030393431636534
32373362366264626462396366346236343635643865316562353532353166653636623736333232
64636563643332363534363733303662633331613664653532346138333334316561646636376465
30656266383536333165353234393665376235646132386630346466633466346464386361666363
31353036356435393039363162323062303831613138306131643936373266323065663865356235
39636465303762386533626231383732636138363863346439346536323663656366353139653661
3931

View file

@ -1,6 +1,7 @@
# ── VISUAL Infrastructure ─────────────────────────────────────────────────────
# main 87.249.49.32 — все сервисы (Traefik, Forgejo, Plane, Vaultwarden, Outline, n8n, CI/CD)
# tools 85.193.83.9 — мониторинг (Grafana, Prometheus, Loki, AlertManager, Uptime Kuma)
# main 87.249.49.32 — core apps (Traefik, Forgejo, Plane, Vaultwarden)
# tools 85.193.83.9 — team tools (Outline, Uptime Kuma)
# mon 188.225.79.34 — monitoring (Grafana, Prometheus, Loki, AlertManager)
[main]
main ansible_host=87.249.49.32
@ -8,14 +9,20 @@ main ansible_host=87.249.49.32
[tools]
tools ansible_host=85.193.83.9
[mon]
mon ansible_host=188.225.79.34
# ── Group for roles that run on ALL servers ───────────────────────────────────
[all_servers:children]
main
tools
mon
[all_servers:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_user=deploy
# ── Legacy alias (keep for backwards compatibility with old playbooks) ────────
[servers]
main ansible_host=87.249.49.32

View file

@ -5,4 +5,4 @@ backup_user: deploy
# Timeweb S3 offsite backups
s3_endpoint: "https://s3.timeweb.cloud"
s3_bucket: "walava-backup"
s3_bucket: "visual-backup"

View file

@ -32,12 +32,6 @@ docker exec plane-db pg_dump -U plane plane \
| gzip > "${WORK_DIR}/data/databases/plane.sql.gz"
log " → databases/plane.sql.gz ($(du -sh "${WORK_DIR}/data/databases/plane.sql.gz" | cut -f1))"
# ── PostgreSQL: Outline ──────────────────────────────────────────────────────
log "Dumping outline-db..."
docker exec outline-db pg_dump -U outline outline \
| gzip > "${WORK_DIR}/data/databases/outline.sql.gz"
log " → databases/outline.sql.gz ($(du -sh "${WORK_DIR}/data/databases/outline.sql.gz" | cut -f1))"
# ── Forgejo data volume (repos, attachments, LFS) ───────────────────────────
log "Backing up Forgejo data..."
docker run --rm \
@ -56,24 +50,6 @@ docker run --rm \
tar czf /backup/uptime-kuma.tar.gz /app/data
log " → volumes/uptime-kuma.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/uptime-kuma.tar.gz" | cut -f1))"
# ── n8n workflows + credentials ──────────────────────────────────────────────
log "Backing up n8n..."
docker run --rm \
--volumes-from n8n \
-v "${WORK_DIR}/data/volumes:/backup" \
alpine:3 \
tar czf /backup/n8n.tar.gz /home/node/.n8n
log " → volumes/n8n.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/n8n.tar.gz" | cut -f1))"
# ── Plane MinIO (uploaded files / attachments) ───────────────────────────────
log "Backing up Plane MinIO..."
docker run --rm \
--volumes-from plane-minio \
-v "${WORK_DIR}/data/volumes:/backup" \
alpine:3 \
tar czf /backup/plane-minio.tar.gz /data
log " → volumes/plane-minio.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/plane-minio.tar.gz" | cut -f1))"
# ── Add restore instructions ─────────────────────────────────────────────────
cat > "${WORK_DIR}/data/RESTORE.md" << 'RESTORE_EOF'
# Restore Instructions
@ -96,9 +72,6 @@ zcat data/databases/forgejo.sql.gz | docker exec -i forgejo-db psql -U forgejo f
# Plane DB
zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane
# Outline DB
zcat data/databases/outline.sql.gz | docker exec -i outline-db psql -U outline outline
```
## Step 3 — Restore volume data
@ -107,17 +80,9 @@ zcat data/databases/outline.sql.gz | docker exec -i outline-db psql -U outline o
docker run --rm --volumes-from forgejo -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/forgejo.tar.gz"
# Uptime Kuma
# Uptime Kuma — extracts /app/data/ into the container
docker run --rm --volumes-from uptime-kuma -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/uptime-kuma.tar.gz"
# n8n
docker run --rm --volumes-from n8n -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/n8n.tar.gz"
# Plane MinIO (uploaded files)
docker run --rm --volumes-from plane-minio -v $(pwd)/data/volumes:/backup \
alpine:3 sh -c "cd / && tar xzf /backup/plane-minio.tar.gz"
```
## Step 4 — Restart services

View file

@ -28,8 +28,3 @@ promtail_image: "grafana/promtail:3.4.3" # https://hub
crowdsec_image: "crowdsecurity/crowdsec:v1.6.8" # https://hub.docker.com/r/crowdsecurity/crowdsec/tags
redis_image: "redis:7-alpine"
uptime_kuma_image: "louislam/uptime-kuma:1" # https://hub.docker.com/r/louislam/uptime-kuma/tags
outline_image: "outlinewiki/outline:0.80.2" # https://hub.docker.com/r/outlinewiki/outline/tags
outline_db_image: "postgres:15-alpine"
outline_redis_image: "redis:7-alpine"
n8n_image: "n8nio/n8n:1.89.2" # https://hub.docker.com/r/n8nio/n8n/tags
outline_mcp_image: "git.{{ domain_base }}/jack/outline-mcp:latest"

View file

@ -8,15 +8,6 @@
mode: "0600"
notify: Restart stack
- name: Deploy Outline .env file
ansible.builtin.template:
src: env.outline.j2
dest: "{{ services_root }}/.env.outline"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
@ -53,19 +44,77 @@
mode: "0644"
notify: Restart stack
- name: Configure CORS on walava-outline S3 bucket (required for browser uploads)
ansible.builtin.shell: |
docker run --rm \
-e AWS_ACCESS_KEY_ID={{ s3_access_key }} \
-e AWS_SECRET_ACCESS_KEY={{ s3_secret_key }} \
-e AWS_DEFAULT_REGION=ru-1 \
amazon/aws-cli:latest \
--endpoint-url https://s3.timeweb.cloud \
s3api put-bucket-cors \
--bucket walava-outline \
--cors-configuration '{"CORSRules":[{"AllowedOrigins":["https://{{ domain_wiki }}"],"AllowedMethods":["GET","PUT","POST","DELETE","HEAD"],"AllowedHeaders":["*"],"ExposeHeaders":["ETag"],"MaxAgeSeconds":3000}]}'
changed_when: false
ignore_errors: true
- name: Deploy Prometheus config
ansible.builtin.template:
src: prometheus/prometheus.yml.j2
dest: "{{ services_root }}/prometheus/prometheus.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Grafana datasource provisioning
ansible.builtin.template:
src: grafana/provisioning/datasources/prometheus.yml.j2
dest: "{{ services_root }}/grafana/provisioning/datasources/prometheus.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Grafana dashboard provisioning config
ansible.builtin.template:
src: grafana/provisioning/dashboards/dashboards.yml.j2
dest: "{{ services_root }}/grafana/provisioning/dashboards/dashboards.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Node Exporter Full dashboard JSON
ansible.builtin.copy:
src: grafana/dashboards/node-exporter-full.json
dest: "{{ services_root }}/grafana/provisioning/dashboards/json/node-exporter-full.json"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy cAdvisor dashboard JSON
ansible.builtin.copy:
src: grafana/dashboards/cadvisor.json
dest: "{{ services_root }}/grafana/provisioning/dashboards/json/cadvisor.json"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Prometheus alert rules
ansible.builtin.template:
src: prometheus/rules/alerts.yml.j2
dest: "{{ services_root }}/prometheus/rules/alerts.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy AlertManager config
ansible.builtin.template:
src: prometheus/alertmanager.yml.j2
dest: "{{ services_root }}/prometheus/alertmanager.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Loki config
ansible.builtin.template:
src: loki/loki.yml.j2
dest: "{{ services_root }}/loki/loki.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy Promtail config
ansible.builtin.template:
@ -76,6 +125,15 @@
mode: "0644"
notify: Restart stack
- name: Deploy Grafana Loki datasource
ansible.builtin.template:
src: grafana/provisioning/datasources/loki.yml.j2
dest: "{{ services_root }}/grafana/provisioning/datasources/loki.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
notify: Restart stack
- name: Deploy CrowdSec acquisition config
ansible.builtin.template:
src: crowdsec/acquis.yaml.j2
@ -85,6 +143,24 @@
mode: "0644"
notify: Restart stack
- name: Deploy Authelia configuration
ansible.builtin.template:
src: authelia/configuration.yml.j2
dest: "{{ services_root }}/authelia/configuration.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy Authelia users database
ansible.builtin.template:
src: authelia/users.yml.j2
dest: "{{ services_root }}/authelia/users.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
notify: Restart stack
- name: Deploy Traefik logrotate config
ansible.builtin.template:
src: logrotate/traefik.j2

View file

@ -22,6 +22,12 @@
- plane/pgdata
- plane/media
- act_runner
- prometheus
- grafana/provisioning/datasources
- grafana/provisioning/dashboards
- grafana/provisioning/dashboards/json
- prometheus/rules
- loki
- traefik/logs
- crowdsec
- authelia

View file

@ -16,43 +16,29 @@
- "{{ plane_redis_image }}"
- "{{ plane_minio_image }}"
- "{{ act_runner_image }}"
- "{{ prometheus_image }}"
- "{{ node_exporter_image }}"
- "{{ cadvisor_image }}"
- "{{ grafana_image }}"
- "{{ alertmanager_image }}"
- "{{ loki_image }}"
- "{{ promtail_image }}"
- "{{ crowdsec_image }}"
- "{{ outline_image }}"
- "{{ outline_db_image }}"
- "{{ outline_redis_image }}"
- "{{ n8n_image }}"
- "{{ uptime_kuma_image }}"
- "tecnativa/postfix-relay"
register: pull_result
changed_when: "'Status: Downloaded newer image' in pull_result.stdout"
retries: 5
delay: 30
until: pull_result.rc == 0
# ── UFW: allow tools Prometheus to scrape exporters on main ──────────────────
- name: Allow tools server to scrape node-exporter
community.general.ufw:
rule: allow
port: "9100"
proto: tcp
src: "{{ ip_tools }}"
- name: Allow tools server to scrape cAdvisor
community.general.ufw:
rule: allow
port: "8080"
proto: tcp
src: "{{ ip_tools }}"
- name: Remove legacy SMTP relay UFW rule (port 1025)
- name: Allow SMTP relay port from tools server
community.general.ufw:
rule: allow
port: "1025"
proto: tcp
src: "{{ ip_tools }}"
delete: true
failed_when: false
comment: "SMTP relay for tools-server Outline"
- name: Deploy Docker Compose stack
community.docker.docker_compose_v2:

View file

@ -26,12 +26,6 @@ networks:
monitoring:
driver: bridge
internal: true
outline-internal:
driver: bridge
internal: true
n8n-internal:
driver: bridge
internal: true
volumes:
forgejo_data:
forgejo_db_data:
@ -40,10 +34,11 @@ volumes:
plane_minio_data:
plane_media:
act_runner_data:
prometheus_data:
grafana_data:
loki_data:
crowdsec_data:
outline_db_data:
outline_redis_data:
n8n_data:
uptime_kuma_data:
services:
@ -377,16 +372,52 @@ services:
- backend
- runner-jobs
# ── Monitoring exporters (metrics scraped by tools Prometheus over network) ──
# Ports exposed: tools server must have UFW rules allowing ip_main:9100/8080
# ── Monitoring Stack ───────────────────────────────────────────────────────
prometheus:
image: {{ prometheus_image }}
container_name: prometheus
restart: unless-stopped
networks:
- monitoring
volumes:
- prometheus_data:/prometheus
- {{ services_root }}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- {{ services_root }}/prometheus/rules:/etc/prometheus/rules:ro
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=30d"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
alertmanager:
image: {{ alertmanager_image }}
container_name: alertmanager
restart: unless-stopped
networks:
- monitoring
volumes:
- {{ services_root }}/prometheus/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
command:
- "--config.file=/etc/alertmanager/alertmanager.yml"
- "--storage.path=/alertmanager"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:9093/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
node-exporter:
image: {{ node_exporter_image }}
container_name: node-exporter
restart: unless-stopped
networks:
- monitoring
ports:
- "9100:9100"
pid: host
volumes:
- /proc:/host/proc:ro
@ -403,8 +434,6 @@ services:
restart: unless-stopped
networks:
- monitoring
ports:
- "8080:8080"
privileged: true
devices:
- /dev/kmsg
@ -415,7 +444,50 @@ services:
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
# ── Logging (Promtail pushes to Loki on tools server) ─────────────────────
grafana:
image: {{ grafana_image }}
container_name: grafana
restart: unless-stopped
security_opt:
- no-new-privileges:true
depends_on:
- prometheus
networks:
- backend
- monitoring
volumes:
- grafana_data:/var/lib/grafana
- {{ services_root }}/grafana/provisioning:/etc/grafana/provisioning:ro
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_DOMAIN={{ domain_dashboard }}
- GF_SERVER_ROOT_URL=https://{{ domain_dashboard }}
- GF_AUTH_ANONYMOUS_ENABLED=false
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
# ── Logging Stack ──────────────────────────────────────────────────────────
loki:
image: {{ loki_image }}
container_name: loki
restart: unless-stopped
networks:
- monitoring
volumes:
- loki_data:/loki
- {{ services_root }}/loki/loki.yml:/etc/loki/local-config.yaml:ro
command: -config.file=/etc/loki/local-config.yaml
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3100/ready"]
interval: 30s
timeout: 5s
retries: 3
promtail:
image: {{ promtail_image }}
container_name: promtail
@ -451,155 +523,75 @@ services:
# ── Discord Bot ────────────────────────────────────────────────────────────
# NOTE: disabled until image is built & pushed to Forgejo registry
# discord-bot:
# image: git.{{ domain_base }}/jack/discord-bot:latest
# container_name: discord-bot
# restart: unless-stopped
# environment:
# DISCORD_TOKEN: "${DISCORD_BOT_TOKEN}"
# DISCORD_APP_ID: "{{ discord_bot_app_id }}"
# FORGEJO_TOKEN: "${FORGEJO_RUNNER_TOKEN}"
# FORGEJO_URL: "https://{{ domain_git }}"
# FORGEJO_REPO: "jack/infra"
# PROMETHEUS_URL: "http://{{ ip_tools }}:9090"
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock:ro
# networks:
# - proxy
# Infrastructure management bot: /status /logs /restart /deploy /metrics /backup
# Image is built and pushed by the discord-bot repo CI/CD
discord-bot:
image: git.{{ domain_base }}/jack/discord-bot:latest
container_name: discord-bot
restart: unless-stopped
environment:
DISCORD_TOKEN: "${DISCORD_BOT_TOKEN}"
DISCORD_APP_ID: "{{ discord_bot_app_id }}"
FORGEJO_TOKEN: "${FORGEJO_RUNNER_TOKEN}"
FORGEJO_URL: "https://{{ domain_git }}"
FORGEJO_REPO: "jack/infra"
PROMETHEUS_URL: "http://prometheus:9090"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy # Discord API (internet)
- monitoring # Prometheus metrics
# ── Walava Landing ─────────────────────────────────────────────────────────
# NOTE: disabled until image is built & pushed to Forgejo registry
# walava-web:
# image: git.{{ domain_base }}/jack/walava-web:latest
# container_name: walava-web
# restart: unless-stopped
# networks:
# - proxy
# ── Outline wiki ────────────────────────────────────────────────────────────
outline:
image: {{ outline_image }}
container_name: outline
# Landing page for walava.io — image built by walava-web repo CI/CD
walava-web:
image: git.{{ domain_base }}/jack/walava-web:latest
container_name: walava-web
restart: unless-stopped
env_file: .env.outline
networks:
- outline-internal
- proxy
# ── Uptime Kuma ────────────────────────────────────────────────────────────
# Мониторинг доступности сервисов + публичная статус-страница
# Доступен по адресу: https://{{ domain_status }}
uptime-kuma:
image: {{ uptime_kuma_image }}
container_name: uptime-kuma
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- backend
- proxy # needs outbound internet for SMTP (Resend) and S3 (Timeweb)
depends_on:
outline-db:
condition: service_healthy
outline-redis:
condition: service_healthy
- proxy # needs internet access for Discord/Telegram notifications
volumes:
- uptime_kuma_data:/app/data
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/_health"]
test: ["CMD", "curl", "-sf", "http://localhost:3001/"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
outline-db:
image: {{ outline_db_image }}
container_name: outline-db
# ── SMTP Relay ─────────────────────────────────────────────────────────────
# Forwards mail from tools-server (85.193.83.9) to Resend SMTP.
# tools-server has outbound SMTP blocked by the VPS provider.
# Listens on 85.193.83.9:1025 (UFW allows only from ip_tools).
smtp-relay:
image: tecnativa/postfix-relay
container_name: smtp-relay
restart: unless-stopped
ports:
- "{{ ip_tools }}:1025:25"
networks:
- proxy
environment:
POSTGRES_DB: outline
POSTGRES_USER: outline
POSTGRES_PASSWORD: {{ outline_db_password }}
networks:
- outline-internal
volumes:
- outline_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 5
- MAILNAME={{ domain_base }}
- MAIL_RELAY_HOST=smtp.resend.com
- MAIL_RELAY_PORT=587
- MAIL_RELAY_USER=resend
- MAIL_RELAY_PASS={{ resend_api_key }}
- MAIL_RELAY_MYHOSTNAME=mail.{{ domain_base }}
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
outline-redis:
image: {{ outline_redis_image }}
container_name: outline-redis
restart: unless-stopped
networks:
- outline-internal
volumes:
- outline_redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── n8n workflow automation ──────────────────────────────────────────────────
n8n:
image: {{ n8n_image }}
container_name: n8n
restart: unless-stopped
networks:
- n8n-internal
- backend
- proxy # needs outbound internet for workflow API calls
volumes:
- n8n_data:/home/node/.n8n
environment:
- N8N_HOST={{ domain_n8n }}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://{{ domain_n8n }}/
- N8N_ENCRYPTION_KEY={{ n8n_encryption_key }}
- N8N_USER_MANAGEMENT_JWT_SECRET={{ n8n_jwt_secret }}
- GENERIC_TIMEZONE=Europe/Moscow
- TZ=Europe/Moscow
- N8N_METRICS=false
- N8N_LOG_LEVEL=warn
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=336
- N8N_DIAGNOSTICS_ENABLED=false
- N8N_VERSION_NOTIFICATIONS_ENABLED=false
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/healthz"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Outline MCP server ───────────────────────────────────────────────────────
# NOTE: image must be built & pushed to git.walava.io/jack/outline-mcp:latest first
# Disabled until Forgejo registry has the image
# outline-mcp:
# image: {{ outline_mcp_image }}
# container_name: outline-mcp
# restart: unless-stopped
# networks:
# - backend
# environment:
# - OUTLINE_URL=https://{{ domain_wiki }}
# - OUTLINE_API_KEY={{ outline_mcp_api_key }}
# - PORT=8765
# - HOST=0.0.0.0
# - LOG_LEVEL=INFO
# logging:
# driver: json-file
# options:
# max-size: "10m"
# max-file: "3"
max-size: "5m"
max-file: "2"

View file

@ -1,41 +0,0 @@
# Outline env — generated by Ansible
NODE_ENV=production
SECRET_KEY={{ outline_secret_key }}
UTILS_SECRET={{ outline_utils_secret }}
# Database
DATABASE_URL=postgres://outline:{{ outline_db_password }}@outline-db:5432/outline
PGSSLMODE=disable
# Redis
REDIS_URL=redis://outline-redis:6379
# App URL
URL=https://{{ domain_wiki }}
PORT=3000
# S3 file storage (Timeweb Object Storage)
AWS_ACCESS_KEY_ID={{ s3_access_key }}
AWS_SECRET_ACCESS_KEY={{ s3_secret_key }}
AWS_REGION=ru-1
AWS_S3_UPLOAD_BUCKET_NAME=walava-outline
AWS_S3_UPLOAD_BUCKET_URL=https://s3.twcstorage.ru
AWS_S3_FORCE_PATH_STYLE=true
FILE_STORAGE=s3
# Auth
AUTH_PROVIDERS=email
# SMTP via Resend (direct — main server has outbound SMTP)
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USERNAME=resend
SMTP_PASSWORD={{ resend_api_key }}
SMTP_FROM_EMAIL=noreply@{{ domain_base }}
SMTP_FROM_NAME=Visual Wiki
SMTP_SECURE=false
# Optional
DEFAULT_LANGUAGE=en_US
RATE_LIMITER_ENABLED=true
ENABLE_UPDATES=false

View file

@ -7,7 +7,7 @@ positions:
filename: /tmp/positions.yaml
clients:
- url: http://{{ ip_tools }}:3100/loki/api/v1/push
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker

View file

@ -89,6 +89,7 @@ http:
service: walava-landing
middlewares: [rate-limit-default]
# ── Cross-server: tools ({{ ip_tools }}) ─────────────────────────────────
wiki:
rule: "Host(`{{ domain_wiki }}`)"
entrypoints: [websecure]
@ -134,27 +135,28 @@ http:
grafana:
loadBalancer:
servers:
- url: "http://{{ ip_tools }}:3000"
- url: "http://grafana:3000"
uptime-kuma:
loadBalancer:
servers:
- url: "http://{{ ip_tools }}:3001"
- url: "http://uptime-kuma:3001"
walava-landing:
loadBalancer:
servers:
- url: "http://walava-web:80"
# ── Cross-server services ─────────────────────────────────────────────────
wiki:
loadBalancer:
servers:
- url: "http://outline:3000"
- url: "http://{{ ip_tools }}:3000"
n8n:
loadBalancer:
servers:
- url: "http://n8n:5678"
- url: "http://{{ ip_tools }}:5678"
middlewares:
# ── Security Headers (applied globally via entrypoint) ─────────────────

View file

@ -1,11 +1,7 @@
---
tools_root: /opt/tools
# Image versions (mirrors services role — keep in sync)
prometheus_image: "prom/prometheus:v3.4.0"
node_exporter_image: "prom/node-exporter:v1.9.1"
cadvisor_image: "gcr.io/cadvisor/cadvisor:v0.52.1"
grafana_image: "grafana/grafana:11.6.1"
alertmanager_image: "prom/alertmanager:v0.28.1"
loki_image: "grafana/loki:3.4.3"
uptime_kuma_image: "louislam/uptime-kuma:1"
outline_image: "outlinewiki/outline:0.80.2"
outline_db_image: "postgres:15-alpine"
outline_redis_image: "redis:7-alpine"
n8n_image: "n8nio/n8n:1.89.2" # https://hub.docker.com/r/n8nio/n8n/tags
outline_mcp_image: "git.{{ domain_base }}/jack/outline-mcp:latest"

View file

@ -1,817 +0,0 @@
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "Prometheus as the datasource is obligatory",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.4.5"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": 14282,
"graphTooltip": 0,
"id": null,
"iteration": 1617715580880,
"links": [],
"panels": [
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 0
},
"id": 8,
"panels": [],
"title": "CPU",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 24,
"x": 0,
"y": 1
},
"hiddenSeries": false,
"id": 15,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(container_cpu_usage_seconds_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name) *100",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "CPU Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:606",
"format": "percent",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:607",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 8
},
"id": 11,
"panels": [],
"title": "Memory",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 9
},
"hiddenSeries": false,
"id": 9,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum(container_memory_rss{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Memory Usage",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:606",
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:607",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 9
},
"hiddenSeries": false,
"id": 14,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum(container_memory_cache{instance=~\"$host\",name=~\"$container\",name=~\".+\"}) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Memory Cached",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:606",
"format": "bytes",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:607",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 17
},
"id": 2,
"panels": [],
"title": "Network",
"type": "row"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 18
},
"hiddenSeries": false,
"id": 4,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"hideEmpty": false,
"hideZero": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"sideWidth": null,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(container_network_receive_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
"hide": false,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Received Network Traffic",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:674",
"format": "Bps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:675",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 18
},
"hiddenSeries": false,
"id": 6,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.4.5",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(rate(container_network_transmit_bytes_total{instance=~\"$host\",name=~\"$container\",name=~\".+\"}[5m])) by (name)",
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Sent Network Traffic",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:832",
"format": "Bps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:833",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"collapsed": false,
"datasource": "${DS_PROMETHEUS}",
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 26
},
"id": 19,
"panels": [],
"title": "Misc",
"type": "row"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {
"align": null,
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "id"
},
"properties": [
{
"id": "custom.width",
"value": 260
}
]
},
{
"matcher": {
"id": "byName",
"options": "Running"
},
"properties": [
{
"id": "unit",
"value": "d"
},
{
"id": "decimals",
"value": 1
},
{
"id": "custom.displayMode",
"value": "color-text"
},
{
"id": "color",
"value": {
"fixedColor": "dark-green",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 27
},
"id": 17,
"options": {
"showHeader": true,
"sortBy": []
},
"pluginVersion": "7.4.5",
"targets": [
{
"expr": "(time() - container_start_time_seconds{instance=~\"$host\",name=~\"$container\",name=~\".+\"})/86400",
"format": "table",
"instant": true,
"interval": "",
"legendFormat": "{{name}}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Containers Info",
"transformations": [
{
"id": "filterFieldsByName",
"options": {
"include": {
"names": [
"container_label_com_docker_compose_project",
"container_label_com_docker_compose_project_working_dir",
"image",
"instance",
"name",
"Value",
"container_label_com_docker_compose_service"
]
}
}
},
{
"id": "organize",
"options": {
"excludeByName": {},
"indexByName": {},
"renameByName": {
"Value": "Running",
"container_label_com_docker_compose_project": "Label",
"container_label_com_docker_compose_project_working_dir": "Working dir",
"container_label_com_docker_compose_service": "Service",
"image": "Registry Image",
"instance": "Instance",
"name": "Name"
}
}
}
],
"type": "table"
}
],
"schemaVersion": 27,
"style": "dark",
"tags": [
"cadvisor",
"docker"
],
"templating": {
"list": [
{
"allValue": ".*",
"current": {},
"datasource": "${DS_PROMETHEUS}",
"definition": "label_values({__name__=~\"container.*\"},instance)",
"description": null,
"error": null,
"hide": 0,
"includeAll": true,
"label": "Host",
"multi": false,
"name": "host",
"options": [],
"query": {
"query": "label_values({__name__=~\"container.*\"},instance)",
"refId": "Prometheus-host-Variable-Query"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 5,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"allValue": ".*",
"current": {},
"datasource": "${DS_PROMETHEUS}",
"definition": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)",
"description": null,
"error": null,
"hide": 0,
"includeAll": true,
"label": "Container",
"multi": false,
"name": "container",
"options": [],
"query": {
"query": "label_values({__name__=~\"container.*\", instance=~\"$host\"},name)",
"refId": "Prometheus-container-Variable-Query"
},
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Cadvisor exporter",
"uid": "pMEd7m0Mz",
"version": 1,
"description": "Simple exporter for cadvisor only"
}

File diff suppressed because it is too large Load diff

View file

@ -7,29 +7,7 @@
group: "{{ deploy_group }}"
mode: "0750"
- name: Create tools subdirectories
ansible.builtin.file:
path: "{{ tools_root }}/{{ item }}"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0755"
loop:
- prometheus
- prometheus/rules
- grafana/provisioning/datasources
- grafana/provisioning/dashboards
- grafana/provisioning/dashboards/json
- loki
- name: Deploy .env file
ansible.builtin.template:
src: env.j2
dest: "{{ tools_root }}/.env"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0600"
# ── Deploy configs and start stack ────────────────────────────────────────────
- name: Deploy docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
@ -38,130 +16,27 @@
group: "{{ deploy_group }}"
mode: "0640"
- name: Deploy Prometheus config
- name: Deploy .env
ansible.builtin.template:
src: prometheus/prometheus.yml.j2
dest: "{{ tools_root }}/prometheus/prometheus.yml"
src: env.j2
dest: "{{ tools_root }}/.env"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
mode: "0600"
- name: Deploy Prometheus alert rules
ansible.builtin.template:
src: prometheus/rules/alerts.yml.j2
dest: "{{ tools_root }}/prometheus/rules/alerts.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy AlertManager config
ansible.builtin.template:
src: prometheus/alertmanager.yml.j2
dest: "{{ tools_root }}/prometheus/alertmanager.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy Loki config
ansible.builtin.template:
src: loki/loki.yml.j2
dest: "{{ tools_root }}/loki/loki.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy Grafana Prometheus datasource
ansible.builtin.template:
src: grafana/provisioning/datasources/prometheus.yml.j2
dest: "{{ tools_root }}/grafana/provisioning/datasources/prometheus.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy Grafana Loki datasource
ansible.builtin.template:
src: grafana/provisioning/datasources/loki.yml.j2
dest: "{{ tools_root }}/grafana/provisioning/datasources/loki.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy Grafana dashboard provisioning config
ansible.builtin.template:
src: grafana/provisioning/dashboards/dashboards.yml.j2
dest: "{{ tools_root }}/grafana/provisioning/dashboards/dashboards.yml"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy Node Exporter Full dashboard JSON
ansible.builtin.copy:
src: grafana/dashboards/node-exporter-full.json
dest: "{{ tools_root }}/grafana/provisioning/dashboards/json/node-exporter-full.json"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Deploy cAdvisor dashboard JSON
ansible.builtin.copy:
src: grafana/dashboards/cadvisor.json
dest: "{{ tools_root }}/grafana/provisioning/dashboards/json/cadvisor.json"
owner: "{{ deploy_user }}"
group: "{{ deploy_group }}"
mode: "0644"
- name: Pull monitoring images
ansible.builtin.command: docker pull {{ item }}
- name: Pull images
community.docker.docker_image:
name: "{{ item }}"
source: pull
loop:
- "{{ prometheus_image }}"
- "{{ alertmanager_image }}"
- "{{ node_exporter_image }}"
- "{{ cadvisor_image }}"
- "{{ grafana_image }}"
- "{{ loki_image }}"
- "{{ uptime_kuma_image }}"
register: pull_result
changed_when: "'Status: Downloaded newer image' in pull_result.stdout"
retries: 5
delay: 30
until: pull_result.rc == 0
# ── UFW: allow main server to reach monitoring services ───────────────────────
- name: Allow main server to reach Loki (Promtail log push)
community.general.ufw:
rule: allow
port: "3100"
proto: tcp
src: "{{ ip_main }}"
- name: Allow main server to reach Prometheus (discord-bot metrics)
community.general.ufw:
rule: allow
port: "9090"
proto: tcp
src: "{{ ip_main }}"
- name: Allow main Traefik to reach Grafana
community.general.ufw:
rule: allow
port: "3000"
proto: tcp
src: "{{ ip_main }}"
- name: Allow main Traefik to reach Uptime Kuma
community.general.ufw:
rule: allow
port: "3001"
proto: tcp
src: "{{ ip_main }}"
- "{{ outline_image }}"
- "{{ outline_db_image }}"
- "{{ outline_redis_image }}"
- "{{ n8n_image }}"
- name: Start tools stack
community.docker.docker_compose_v2:
project_src: "{{ tools_root }}"
state: present
pull: never
pull: missing
remove_orphans: true
retries: 3
delay: 15
register: compose_result
until: compose_result is succeeded

View file

@ -1,157 +1,150 @@
# Tools stack — generated by Ansible
# Do not edit manually; re-run ansible-playbook playbooks/tools.yml
# Monitoring: Prometheus, Grafana, Loki, AlertManager, Uptime Kuma, node-exporter, cAdvisor
networks:
monitoring:
# front — non-internal: needed for Docker port binding to work (expose ports to host)
# Docker does not create DNAT rules for containers only on internal networks
front:
driver: bridge
outline-internal:
driver: bridge
internal: true
n8n-internal:
driver: bridge
internal: true
volumes:
prometheus_data:
grafana_data:
loki_data:
uptime_kuma_data:
outline_db_data:
outline_redis_data:
n8n_data:
services:
# ── Prometheus ─────────────────────────────────────────────────────────────
prometheus:
image: {{ prometheus_image }}
container_name: prometheus
# ── Outline wiki ────────────────────────────────────────────────────────────
outline:
image: {{ outline_image }}
container_name: outline
restart: unless-stopped
networks:
- monitoring
ports:
- "127.0.0.1:9090:9090" # exposed to main via UFW rule for discord-bot
volumes:
- prometheus_data:/prometheus
- {{ tools_root }}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- {{ tools_root }}/prometheus/rules:/etc/prometheus/rules:ro
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=30d"
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
- "--web.console.templates=/usr/share/prometheus/consoles"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:9090/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
alertmanager:
image: {{ alertmanager_image }}
container_name: alertmanager
restart: unless-stopped
networks:
- monitoring
volumes:
- {{ tools_root }}/prometheus/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
command:
- "--config.file=/etc/alertmanager/alertmanager.yml"
- "--storage.path=/alertmanager"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:9093/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
# ── Exporters (monitor the tools host itself) ───────────────────────────────
node-exporter:
image: {{ node_exporter_image }}
container_name: node-exporter
restart: unless-stopped
networks:
- monitoring
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.sysfs=/host/sys"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
cadvisor:
image: {{ cadvisor_image }}
container_name: cadvisor
restart: unless-stopped
networks:
- monitoring
privileged: true
devices:
- /dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
# ── Grafana ─────────────────────────────────────────────────────────────────
grafana:
image: {{ grafana_image }}
container_name: grafana
restart: unless-stopped
security_opt:
- no-new-privileges:true
depends_on:
- prometheus
networks:
- monitoring
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- {{ tools_root }}/grafana/provisioning:/etc/grafana/provisioning:ro
env_file: .env
networks:
- outline-internal
- front # needed for host port binding
ports:
# Exposed only to main Traefik (access controlled by UFW)
- "{{ ip_tools }}:3000:3000"
depends_on:
outline-db:
condition: service_healthy
outline-redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/_health"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
outline-db:
image: {{ outline_db_image }}
container_name: outline-db
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_DOMAIN={{ domain_dashboard }}
- GF_SERVER_ROOT_URL=https://{{ domain_dashboard }}
- GF_AUTH_ANONYMOUS_ENABLED=false
POSTGRES_DB: outline
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${OUTLINE_DB_PASSWORD}
networks:
- outline-internal
volumes:
- outline_db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
interval: 30s
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 3
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Loki ────────────────────────────────────────────────────────────────────
loki:
image: {{ loki_image }}
container_name: loki
outline-redis:
image: {{ outline_redis_image }}
container_name: outline-redis
restart: unless-stopped
networks:
- monitoring
ports:
- "3100:3100" # exposed to main for Promtail log ingestion
- outline-internal
volumes:
- loki_data:/loki
- {{ tools_root }}/loki/loki.yml:/etc/loki/local-config.yaml:ro
command: -config.file=/etc/loki/local-config.yaml
- outline_redis_data:/data
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3100/ready"]
interval: 30s
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
retries: 5
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Uptime Kuma ─────────────────────────────────────────────────────────────
uptime-kuma:
image: {{ uptime_kuma_image }}
container_name: uptime-kuma
# ── Outline MCP server ───────────────────────────────────────────────────────
# MCP server exposing Outline wiki to Claude/AI clients (port 8765, internal only)
outline-mcp:
image: {{ outline_mcp_image }}
container_name: outline-mcp
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- monitoring
- front # needed for host port binding
ports:
- "3001:3001"
- "127.0.0.1:8765:8765"
environment:
- OUTLINE_URL=https://{{ domain_wiki }}
- OUTLINE_API_KEY={{ outline_mcp_api_key }}
- PORT=8765
- HOST=0.0.0.0
- LOG_LEVEL=INFO
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── n8n workflow automation ──────────────────────────────────────────────────
n8n:
image: {{ n8n_image }}
container_name: n8n
restart: unless-stopped
networks:
- n8n-internal
- front # needed for host port binding
ports:
# Exposed only to main Traefik (access controlled by UFW)
- "{{ ip_tools }}:5678:5678"
volumes:
- uptime_kuma_data:/app/data
- n8n_data:/home/node/.n8n
environment:
- N8N_HOST={{ domain_n8n }}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://{{ domain_n8n }}/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_USER_MANAGEMENT_JWT_SECRET=${N8N_JWT_SECRET}
- GENERIC_TIMEZONE=Europe/Moscow
- TZ=Europe/Moscow
- N8N_METRICS=false
- N8N_LOG_LEVEL=warn
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=336
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3001/"]
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/healthz"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

View file

@ -1,2 +1,47 @@
# Generated by Ansible — do not edit manually
GF_SECURITY_ADMIN_PASSWORD={{ grafana_admin_password }}
# Outline env — generated by Ansible
NODE_ENV=production
SECRET_KEY={{ outline_secret_key }}
UTILS_SECRET={{ outline_utils_secret }}
# Database
DATABASE_URL=postgres://outline:{{ outline_db_password }}@outline-db:5432/outline
PGSSLMODE=disable
# Redis
REDIS_URL=redis://outline-redis:6379
# App URL
URL=https://{{ domain_wiki }}
PORT=3000
# S3 file storage (Timeweb Object Storage)
AWS_ACCESS_KEY_ID={{ s3_access_key }}
AWS_SECRET_ACCESS_KEY={{ s3_secret_key }}
AWS_REGION=ru-1
AWS_S3_UPLOAD_BUCKET_NAME=visual-outline
AWS_S3_UPLOAD_BUCKET_URL=https://s3.timeweb.cloud
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private
FILE_STORAGE=s3
# Auth
AUTH_PROVIDERS=email
# SMTP via relay on main server (tools-server has outbound SMTP blocked)
SMTP_HOST={{ ip_main }}
SMTP_PORT=1025
SMTP_FROM_EMAIL=noreply@{{ domain_base }}
SMTP_FROM_NAME=Visual Wiki
SMTP_SECURE=false
# Outline DB password (used in docker-compose)
OUTLINE_DB_PASSWORD={{ outline_db_password }}
# Optional
DEFAULT_LANGUAGE=en_US
RATE_LIMITER_ENABLED=true
ENABLE_UPDATES=false
# n8n secrets
N8N_ENCRYPTION_KEY={{ n8n_encryption_key }}
N8N_JWT_SECRET={{ n8n_jwt_secret }}

View file

@ -1,13 +0,0 @@
# Generated by Ansible — do not edit manually
apiVersion: 1
providers:
- name: default
orgId: 1
folder: ""
type: file
disableDeletion: false
updateIntervalSeconds: 30
allowUiUpdates: false
options:
path: /etc/grafana/provisioning/dashboards/json

View file

@ -1,10 +0,0 @@
# Generated by Ansible — do not edit manually
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: false
editable: false

View file

@ -1,10 +0,0 @@
# Generated by Ansible — do not edit manually
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false

View file

@ -1,36 +0,0 @@
# Generated by Ansible — do not edit manually
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 30d
compactor:
working_directory: /loki/retention
delete_request_store: filesystem
retention_enabled: true

View file

@ -1,38 +0,0 @@
# Generated by Ansible — do not edit manually
global:
resolve_timeout: 5m
route:
group_by: [alertname, severity]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: all
receivers:
- name: all
telegram_configs:
- bot_token: "{{ alertmanager_telegram_token }}"
chat_id: {{ alertmanager_telegram_chat_id }}
message: |
{{ '{{' }} range .Alerts {{ '}}' }}
{{ '{{' }} if eq .Status "firing" {{ '}}' }}🔴{{ '{{' }} else {{ '}}' }}🟢{{ '{{' }} end {{ '}}' }} *{{ '{{' }} .Labels.alertname {{ '}}' }}*
{{ '{{' }} .Annotations.summary {{ '}}' }}
{{ '{{' }} .Annotations.description {{ '}}' }}
{{ '{{' }} end {{ '}}' }}
parse_mode: Markdown
discord_configs:
- webhook_url: "{{ discord_webhook_alerts }}"
title: >-
{{ '{{' }} if eq (index .Alerts 0).Status "firing" {{ '}}' }}🔴 Alert{{ '{{' }} else {{ '}}' }}🟢 Resolved{{ '{{' }} end {{ '}}' }}
message: |
{{ '{{' }} range .Alerts {{ '}}' }}
**{{ '{{' }} .Labels.alertname {{ '}}' }}**
{{ '{{' }} .Annotations.summary {{ '}}' }}
{{ '{{' }} .Annotations.description {{ '}}' }}
{{ '{{' }} end {{ '}}' }}
inhibit_rules:
- source_matchers: [severity="critical"]
target_matchers: [severity="warning"]
equal: [alertname]

View file

@ -1,49 +0,0 @@
# Generated by Ansible — do not edit manually
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
instance: "{{ domain_base }}"
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]
# tools server metrics
- job_name: node-exporter-tools
static_configs:
- targets: ["node-exporter:9100"]
labels:
host: tools
- job_name: cadvisor-tools
static_configs:
- targets: ["cadvisor:8080"]
labels:
host: tools
- job_name: alertmanager
static_configs:
- targets: ["alertmanager:9093"]
# main server metrics (scraped over network)
- job_name: node-exporter-main
static_configs:
- targets: ["{{ ip_main }}:9100"]
labels:
host: main
- job_name: cadvisor-main
static_configs:
- targets: ["{{ ip_main }}:8080"]
labels:
host: main

View file

@ -1,86 +0,0 @@
# Generated by Ansible — do not edit manually
groups:
- name: host
rules:
- alert: HighCPULoad
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Высокая нагрузка CPU ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "CPU загружен более 85% на протяжении 5 минут."
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "Высокое использование RAM ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "Использование RAM превысило 85%."
- alert: CriticalMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 95
for: 2m
labels:
severity: critical
annotations:
summary: "Критическое использование RAM ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "RAM заполнена на 95%+. Возможны OOM kills."
- alert: DiskSpaceWarning
expr: (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|aufs"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|aufs"})) * 100 > 75
for: 5m
labels:
severity: warning
annotations:
summary: "Заканчивается место на диске ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "Диск {{ '{{' }} $labels.mountpoint {{ '}}' }} занят на {{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%."
- alert: DiskSpaceCritical
expr: (1 - (node_filesystem_avail_bytes{fstype!~"tmpfs|overlay|aufs"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay|aufs"})) * 100 > 90
for: 2m
labels:
severity: critical
annotations:
summary: "Критически мало места на диске ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "Диск {{ '{{' }} $labels.mountpoint {{ '}}' }} занят на {{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%."
- alert: SwapUsageHigh
expr: (1 - (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes)) * 100 > 50
for: 5m
labels:
severity: warning
annotations:
summary: "Высокое использование swap ({{ '{{' }} $value | printf \"%.0f\" {{ '}}' }}%)"
description: "Swap используется более чем на 50% — RAM под давлением."
- name: containers
rules:
- alert: ContainerDown
expr: absent(container_last_seen{name=~".+"}) or time() - container_last_seen{name=~".+"} > 60
for: 2m
labels:
severity: critical
annotations:
summary: "Контейнер {{ '{{' }} $labels.name {{ '}}' }} недоступен"
description: "Контейнер не отвечает более 2 минут."
- alert: ContainerHighMemory
expr: (container_memory_usage_bytes{name=~".+"} / (container_spec_memory_limit_bytes{name=~".+"} > 0)) * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "Контейнер {{ '{{' }} $labels.name {{ '}}' }} использует 90%+ памяти"
description: "Контейнер близок к mem_limit — возможен OOM kill."
- alert: ContainerRestarting
expr: increase(container_last_seen{name=~".+"}[5m]) == 0 and rate(container_cpu_usage_seconds_total{name=~".+"}[5m]) == 0
for: 0m
labels:
severity: warning
annotations:
summary: "Контейнер {{ '{{' }} $labels.name {{ '}}' }} возможно перезапускается"
description: "Контейнер не активен — проверьте docker ps."

View file

@ -1,7 +0,0 @@
# Секреты и state — никогда не коммитить
terraform.tfvars
*.tfstate
*.tfstate.backup
.terraform/
.terraform.lock.hcl
crash.log

View file

@ -1,9 +0,0 @@
output "main_ip" {
description = "IP main-сервера"
value = "87.249.49.32"
}
output "tools_ip" {
description = "IP tools-сервера"
value = "85.193.83.9"
}

View file

@ -1,14 +0,0 @@
terraform {
required_version = ">= 1.5"
required_providers {
twc = {
source = "timeweb-cloud/timeweb-cloud"
version = "~> 1.0"
}
}
}
provider "twc" {
token = var.timeweb_token
}

View file

@ -1,25 +0,0 @@
# Серверы
resource "twc_server" "main" {
name = "main"
comment = "Основной: Traefik, Forgejo, Plane, Vaultwarden, Outline, n8n, CI/CD"
os_id = 99
preset_id = 2453
lifecycle {
prevent_destroy = true
ignore_changes = [is_root_password_required]
}
}
resource "twc_server" "tools" {
name = "tools"
comment = "Мониторинг: Grafana, Prometheus, Loki, AlertManager, Uptime Kuma"
os_id = 99
preset_id = 2449
lifecycle {
prevent_destroy = true
ignore_changes = [is_root_password_required]
}
}

View file

@ -1,27 +0,0 @@
# S3 Object Storage
# Импортировано через:
# terraform import twc_s3_bucket.backup 481333
# terraform import twc_s3_bucket.outline 481335
resource "twc_s3_bucket" "backup" {
name = "walava-backup"
type = "private"
preset_id = 2669
lifecycle {
# name/type write-once поля, нельзя менять после создания
ignore_changes = [name, type]
prevent_destroy = true
}
}
resource "twc_s3_bucket" "outline" {
name = "walava-outline"
type = "private"
preset_id = 2669
lifecycle {
ignore_changes = [name, type]
prevent_destroy = true
}
}

View file

@ -1,2 +0,0 @@
# Скопируй в terraform.tfvars (он в .gitignore)
timeweb_token = "your-api-token"

View file

@ -1,5 +0,0 @@
variable "timeweb_token" {
description = "Timeweb Cloud API token"
type = string
sensitive = true
}