Merge 'feat/notesnook-k8s': Notesnook on k3s + sealed secrets
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
# cluster-secrets
|
||||
|
||||
SealedSecrets for credentials belonging to components **not** deployed from this
|
||||
repo's ArgoCD apps (cert-manager, argocd itself, and solitaire — whose manifests
|
||||
live in the Ferrous-Solitaire repo). They are committed here **encrypted**, purely so
|
||||
the cluster's secrets are reproducible after a rebuild.
|
||||
|
||||
No ArgoCD Application watches this directory; these are applied manually:
|
||||
|
||||
kubectl apply -f apps/cluster-secrets/
|
||||
|
||||
## Disaster recovery
|
||||
|
||||
These are useless without the sealed-secrets controller's private key. Restore it
|
||||
FIRST, or a fresh controller will generate a new key and none of these can be decrypted:
|
||||
|
||||
gpg -d /mnt/bulk/backups/sealed-secrets/master-key-YYYYMMDD.gpg | kubectl apply -f -
|
||||
kubectl -n kube-system rollout restart deploy/sealed-secrets-controller
|
||||
kubectl apply -f apps/cluster-secrets/
|
||||
|
||||
Passphrase for that file lives in the password manager. It is not recorded anywhere else.
|
||||
|
||||
## Contents
|
||||
| File | Secret | Notes |
|
||||
|---|---|---|
|
||||
| cloudflare-api-token-sealedsecret.yaml | cert-manager/cloudflare-api-token | DNS-01 ACME; TLS renewal depends on it |
|
||||
| argocd-gitea-repo-sealedsecret.yaml | argocd/gitea-rusty-solitare | repo creds; needs label argocd.argoproj.io/secret-type=repository |
|
||||
| solitaire-gitea-registry-sealedsecret.yaml | solitaire/gitea-registry | image pull secret (dockerconfigjson) |
|
||||
| solitaire-matomo-sealedsecret.yaml | solitaire/matomo-secret | |
|
||||
| solitaire-secrets-sealedsecret.yaml | solitaire/solitaire-secrets | |
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: gitea-rusty-solitare
|
||||
namespace: argocd
|
||||
spec:
|
||||
encryptedData:
|
||||
password: AgBhlHGmiLheVHJuHdg7nLAeriCka35zqVhi5VeugM+BT3JuMiFnZs7riC1U6BbCRwkVFZGA1ecegelKowtkwvohlegGWLIx0GJIfLKA2uuRqD9JdPc7x9x9+hPu8OqE9/vZrV4ojQ0BtD1C/XQDxHmVGn0p6doxIwoiQJMPNYceuvWiVEqMky93mX//8/DbgT/Wi8qK74OhI32oKgPwh9+AbF+kB3N+gnZHk68eETvrmQVMMHGy6JcjbknTdIThU5DI9OgUxhuPZO9kylD9A5M2cSdFVvuWb3Zp86aMO/fnLJxgPSnpqXYDJp4E1dxIpu5neq1OwVyNQwrbUMQKvjsIb9cOzhAojxoyruzzOfadYVafGUtiIdNQU4U1IQJPlSRZyFAYmOAiN+dF9ZokNtSdPEYJZijPovw06nXo+xvuO8JoK5lh4l6XbvN3ilRIdaBeJGGsjJrH0kf3C5vqD0fkBtopymZuhj74joo7MaDAXMlWaQqN/ER28vAfVcxz8mZ/673Td6a8AM4WLbL30IrJJOWrgBvrqbr0peXfXkDVgCXNDaG/PlwirhOzpc2E6kqppAZrqLvL2Seuo57pcnc79mUU79saewNeiHnv/t8lZhfXJ2Ya5UDqu3lha4mhckYb8OOSwk+GpJ8CUZqOJ1j4oruQaUtFb0y1l0lesLQL6CE38t2kyidMhSu05cg2NUqpZGH8xRLCClYjt0nHpfM3hZqp9Ka8coCQ9lVTHf0Kh88DkB8ZlXep
|
||||
type: AgAPt1KdfoqkWYyAcGC7NmTow6+Emr3Uvav1vmYyGUQpiFpFTcmnpMwQBnrZ8hTLBvXNVgQvhgkl9iGHUr3CESd/nFkd68hDg5PN3mAL3fBn8Ng0e3ctD1zWYQSd2v1MqhIARRM3vOutAx2n2lpSJkEhwHGIadG+fdW1u/apqMuiQIAU9V15/rilY/riEJdZOZm+cRUWcdSHsR0lbkQ3wQWVoH9GT8yl+IUAhDjoZad+DuLCjvlC8oOWXtToGuKkcFgVVHow+3aXY49oXm8mBqdj2y+/dcBPhuw3RTKlVNYn4OR7AqOtmWvh88gghKXDSoS12QADyggNKxaLBVc5HA0CMe5hfJDPf7gIZpx32HBOxPtwLQkycfw9QFiZgkz61wu8htNO09kpZvq2TmuKKbae1vfat8iFmAljIH4xBlMFGRPvjZLm3psOgFZo+XYUZogYqvWlnmA4/H8Pj3fxRmjZohcwbR333jzBck9M74/d19Qv7Oyini+BTmk9iZwU2D9jZL82AjFc9fOgvcU57aMZ1UdOLLxLQwAUCd41flv0vR+7s30gyh0wKvc/ShSrGJUbgegS6JqoncuRu6thOjUuP6xeUCcWgM9xkg2L7N6Pzo/jwVPNUcxFyZ9ZLZXVtJEQubAXDze7Soui/VJAT1CEtH+9eLceRLk3xoIPnrWBK6kpxs5MrltvS8W/+9UCuc3GTAY=
|
||||
url: AgCCMYfzUfvm3BlVnaCUu0FKglk77trG4x2EZqK4+PsYmPtB4czZmerMaqFk/RXGWR/pYa/EZL/CfJRdZE+qA3fJHjKWSKpBV5mfb2s8GaEHQl6kBUgIaDiCj7MCIu25b00zHe5agcZBUydD3T+V7ABtMhZTkQvUA+4OZjzQhdas0n6F/hDWBlhWQAzEY2Hj4KRY9r7thyuWYK2m3a4dCXepry2WAGG/sYn4qtD487NOQCNJ9qedg1eI0o0zbL45ErrLLJ3kIhC0upyEMekF85fVgUVu9uTRGvRzdibk55VgqrJPEZwxn3DHvqrJp2e2NYnY6Vo2dpa1avCwdORxA/pK5m4NjetTlD78N2+rJujuAODdglQQXEbe5Kscp0CChI5+/cVPD/gFsnt6YHiEohkMHYT7w+Aa+NWpikcL+QH7pQfKoW+LQKTM89eeVhcgDHsYavqk2oIL/X8+j2I3nFGJPiqkrAgbV95u7SO0tTycfq9m8+hs2yUodxAd2wUxOKk516COTYQ6WzpPXKTCje92m/XqtshoqF4PfH1Fxdf3bE6DS+X8XFOL3z+nR/8neaPbX0VC4kTDks3I2slc+uGRr7YZM+Wv9shsQzJXVgbbJYZgXJS0bwuvUhnc+BMUixPKMx772TjYDvXzsi/xubYyAsIT6KOyzJ+AyT+M7f+754vdrM6uinNnLpSbYN6J1MJ4WNhWluJidpYuBwhFCYg5V/71T6P62lgepaqPkwwHggwHwfVkRQfc7KqWimcfbS4HSFQ=
|
||||
username: AgAOrTTBYkYzZqSHaj03zUnpkSB8daMP0CoGGfw9dWwDK/BNgRzEdkTEGpCKwtJN5ojKTjZTm9q014clw8gXTz2sX5k/NFFF3L9UxeRksrixJUtzLe9deOah+XvurxalhDXHClBIWCipqUGmqdShPOYY8vLHVuXzmVPstLiW58eUmnG/Wafo95weUpFr078rZUKu3+f7DkFP9j+3HqJAsOZsEOjhfCtvmhaOjwHVBldXBR964R1H52w/Jbr3WILNEKSso9/fZD279cvbdBESd7NPuj6UM60cZE8NB5rV6cOkHT9yncFBRq2co13lCo34Xl/4fpqRfF1Dq41DiO8Cm1n5i7EJeY5XyfmKl+Eq6FEGuDxQBVdxFtQfSERICk4g+GJkzJdDhaESzTMeANqLM5alTnIrn/MX+dhCnmD3N6VTYwN8Xiso4CTRPUEwwQqf5CY+prgWQLTUoqomfS4m81C9JCDqKKwKj0mc9df+rGzfIQXNR8W0ETcgitr/9TxTtGMVcU8wanNgNQbV8vu+AKmJZPgXCs7ghqLarzXuTPPPrF/12gtvG77+Gpa/Qh3RsUq3v35fbZZHyy2Evg4D18Rt7nUmvf3ao5QVgG/vI60WjM2TNmNtBmCjrbzGGntZuL3om4LZAV9EIhDDWL6ISdaUtgubrRpGSW7lNgfemi8NIkTq5hd/o/vK8j3ozOsLacvsRaMDnrK3LP8=
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
labels:
|
||||
argocd.argoproj.io/secret-type: repository
|
||||
name: gitea-rusty-solitare
|
||||
namespace: argocd
|
||||
type: Opaque
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: cloudflare-api-token
|
||||
namespace: cert-manager
|
||||
spec:
|
||||
encryptedData:
|
||||
api-token: AgDB+icQdE9haTjqxdeXMFJJkDteMpN+jIjgsbT0RQo55rx8e4U6y4FgBcadOovNDF9g4slD4xqalcJVLBeJWvEZ80wOBhzY+9iP+m8HUPzmm67XWCJgnADtWoBjXqZT6NgUoxNb8MMDrFXdRPatM74o3cbVOcp7dIumOEgo+IML0+TdyIXIkSkjMaPTBQZwkxJ5SQs7WQfnRQw1rTxc9lti49LtH3tELWWLK8MdCOtj23OU4YkbzdbZsqymsiHTYJTK80YyL8bP+TjEMp6ZFstkvEvk8Xk6JNiuPqymiQLTK3y9MOfxK6Picux4Q5KGvWoi3N3D3Ol3I5mwsCXGlZA2WfDIyl86VcrYEphKqouxD/dMNJRJW0vzxbtNiFHBBQDulKfmWs9QVYvA2lD+fdPC34CbvyzyVCBVgg9bp/4OUHWkq0Koo5PDVGgIO7ylHP3YHu271koLNmcIKLBKnMlVnhX5RDVjAk48sFK9+rla6mDiQ19xJpd4P5ep1NxRe9NV0DtFrsRVLsbvPe27P0pwICevG/9OPzfDHQyd6ScGzKM+ltzpi6qgyLudu2bb+0rQ83r6EEvCW654nTGatlGaIbyG1n3cs7V2TlMYRyUYD8IjVXpRoVbYqnxMK2PoGL9ZgKGBoYTKabGYZnl9ngUBHwipEPXZ1ShxmvMn3/l1Mjr1thUlO2ktRMo19gGGAUulnFPoHycY7DxFwyxFa9vcHH//oejC20Lut3kq6cJCYt98WtOtXWqaY45t0gsEcnX89nvkyA==
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: cloudflare-api-token
|
||||
namespace: cert-manager
|
||||
type: Opaque
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: gitea-registry
|
||||
namespace: solitaire
|
||||
spec:
|
||||
encryptedData:
|
||||
.dockerconfigjson: AgC4qgegUdhckd0IVypEjGhMEshxPaZ1Xk6JYbmxZqFyzdIQs72Uv/HxIYTwnfMKcKPd75mkFz6wi4I5yAFRnZDs3JFMujHejIXXAxNYB+mp9jTCDGGS+V4V84zEbdNNuXFBZ034LrnOMeOhn14+ieq22FZWX4w/pnjPpuMGpWL2aunG4nCwoRikXMcvZfVptGjfrYUDEYksCGPcoIlGaV9xuMRQcdt2gWeecs0UdZtTDVBUOVNX4CnnZX2zrTdEX6MCooPqUsv4ZMjDUhjSeLUDgNeAZde0jhbc3wSdjedeeUOqlLTfYgeVRaABUaaslVsBAqTp7MTDjhBYpXl9IRJkiV/RTaC+6vlL1Gku8rva9B2RjORTWL2+IGxiFWmyKnkAR1OxJIkH4VpuiimpWV5eVTNNBSH80k+rquYA8KOjn8aJQ6ZxXN8kOSz3xicNOjZpSp+6eCPwSFUEJePSA/z0JaKWtVYk+rB24D2FHqUkVwOV/X1AG65zsHG1gTUNdr/2k42jB7SOHsBr/0nCgvuJoXftxXnLLinKmBDKa3hHhG7Wjqq5p3eu1MWAADvn0p4UiM/mhomCPHP9oPiuAiiYPMU6Z3nL2eAT2vA0i9gikv9vrk0e6HEtCW2FUlk7JtPZjJCJwKjqlAPu5dceS+YD/eETLX1h1AwugzDbYjtasfiRoQ+AiOqyWitNrds6Dcqk8SlMsWsGf/SRfODR1Dh/JFX6du+dyNzcyJM6L2s0dSCCRuBcUPi2NrjNXbwcIVOHepPLRXyz4KuGNH6pF6ePLpdlxH5DuatTvcaeTKdEtLjwW2YUmK/L66DldrFs5OhGpD02L+LPWYHjrARa/yCl7NWTU7lfzvhGLukVZ2nJG7INYRvPy9qnHIYLRdwpdK+t0UaMCWOgeeThHF4PWn/+BZEjpSk0jDsqj4ETCjtf0NonqhONceeVdg==
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: gitea-registry
|
||||
namespace: solitaire
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: matomo-secret
|
||||
namespace: solitaire
|
||||
spec:
|
||||
encryptedData:
|
||||
MATOMO_ADMIN_PASSWORD: AgCfXf0iM1z1Z3RFkWV1De+u5hCwR5nJyH8OJJy95vboJReTx+pKph4nkHSX1dCOSkm1rDBXu/hAyzq2bytXG7qa7xLMtQLpBl0X/Q/b1tI+G+hwDolOQSzKUSuKghf9n0d7wDBHho1n5JPptTYuyC1jhRzugw9v3lXXzUHT/vS4sypVJ9QGj8fBoyRrld0LLTTooRIV5l0+R3daGrk8EJyV+cx300aFQ1xYBCMgjhexW5lVsbTtYNSZNx0aejPt0pPeOBqZbDHTBKXQA4mXP0VvCHyB6LanZ2oJImbJXyUVKM4uxKrICSVdCPdyOWk22rh4sm1DalmPSAYOS5aEKWOlcX87YCOByamiFF3J/vbtolscqcpWQOE9dv1fBNW8hHEHRmneNyH5UEn7q3Bz3ruqotl1b4wq9tmr5cyYCAZPRXWXKLr6oXr+eGjU0ZliAS/fIJ/VF7MvQpqelS10x+MSSnTV8u+ju9sZuQkRMWVdy/OPFj6S4pxZTfuqwF/Bsptg+TL+kXoAf5zKJjBwEUlEDGkoevGwryADLsITd10kc86zDhrjdLrh7Y5RcKIuXe8H51mzCTHuThB3oxEVQEsgftRudRwvubRmYg3/9HC/k7TSFVX2fmBi+fk3YVTdvxldrRj0+UaRPm+48Ohuxvqccj5N1dFgZXL9Z3TRP6EnuxY5Ehm/dVJx+NXQEH9mGpWJ0lVJAk54i2B+PzbEDYsW1qRaEnXXNkw=
|
||||
MYSQL_DATABASE: AgAH48u2q6zQrN5PaWFRZWssUzStVmIvydUQMZSMqEkrCUK91RzOla0v6Yx9pV1lxlPrqW5NJQ3vQxD7VI4YYiD3FL3DlLFru2wgAT5emvU/UvZnUC6wh5zAxQNjaDPMbjinzVHflKHTztKSyXnnn8j7E2uTAAiRbFpjE94uckBVAxbrUohk62yTFThJ7Y08MV48RE2v5NugWumyisKoMjpo7Wovrj76a17yLO80QQ4Af5Fnk2sAw9/hk4SFrCl5OXdOGq71wtCw8jNAsqHZRlM6cXkcRiGYoFKWTgsW+h/VnSEbuWuEAyF71u/hnre6mSk8Ci5k+VGkTh37afaIcjNdihH8EcWeubOIIxVm7f3LCQ4S9dXMalqG3Frc0QX2zH5eZL0qEqy1zEJ8weXZkgMy6WspCWegnVvfubVZzvrUlRu+oZDpt4SpiKiEWh9g3R5GImWXPrmVm5feet0A7Ff9QhhLdLTl2K/mqTiK5kj0mfbmXnnBvGhh8YNaa5CBTlE3C/5TH/Cop2Qno4/xsgvAqMr5qSd+xUy65S94pOdlc38ngNNsWe71/jfP49u/szO2v/OHnXE2JbhFArAeMRYvcLKIxf73Us/IiliKwIRdFWO2cYknD6Z2FplM9IoC9BmTSgZZMagz0+SnsZBVcVP+K7yizKFVtj5rTjcsHSvqRaAFrOxyiqPAdI7yQTOa/QYHIpHD4f8=
|
||||
MYSQL_PASSWORD: AgC2K1DumViJ6BO8+ACM7ayaE6R46mEiPcFmftjF1wHGaSLYejk0sqJWdh1DHWo2Xa1jk7CnCZBmhZmbbbteDR/E2hKEDfDa3YTQIBVIn+G2blEsSmG/tgnbhW/ZZUSwF/Z/i2I+gMbQDUeKsnSSZ2I+dOo/jVnCfps3xz/9s+ChhrM7mqc9J1Qumh4rtIAd8X+cXPq20rHUMfKm8jt4m/w7UY0DGg5CuoHswrfteo40LtW8KcP4Pc4N/b7uDCdZY54iEzg4e2EL/QlayX8PFiqgRzOHr/fMZ6Z7wWCEiP13V+gPdHaajdqJzlz0QgwvxI5EUOv6Pl2efIfoitOnCvQz7cw7cpT6iapCi9SY65kWRlrDYmybpfP06Jvk39r3HtUzLNEn9cadagbVg1zsjTm/EtMvNPfwY5MQdra0rbQy2b/WI0U8V5Olf9LvrW+y6kRs+YGFKY+FO1CNrWD2N9i/0K5jQVHPx5p3LZPyILKmDZN8ndPDNAGfjRmIC7WHPHRm3sY55P0xc5fI9hb9qss+2BfH70cRu+rdylO70pIcrV/px6V4xTI76MXVp90j4lxbTmwTLggJY6aZoh8NOCU3oaHGwzCXwTDmVpAzgx5amLuG8l4VM8dqBOke7FB0x8Q+2VTK0OEqFiYUVRY0KIwc8i7fXziR0Y/MMbzMyY0EjpoE+kOjNSKgrCxnWxoBCO/0rmlPqeWP1rrbh4h4EfaDIaZ/YG0XsiA=
|
||||
MYSQL_ROOT_PASSWORD: AgCiCZzjb0xHh/ZttsDcnWp6jauP5wBYEoFNc5yRJ+WbSiEBB/ayTHP23XJUH6tBDs9xTGjvoXsMJi5yAX2hxMT/fQ0tlTTzoGlwq2fNdWGkqAL1qkOxLRRyoJhnDA0E98sb6v4LBxWCWlCCBgBrZNi2TMpRI+M5htrEfWXcdUa4pPqGDh5Wd1P7z/DLdmtyAGSIIq3BS6T5dnNaobJj4ya/X9PamzZP9Ck3A98qKJnQpHgUKOgm9zWGx/tpxXpru4oNmYvGxDC6Og7XxfVvBp+eBRcWYMuE6ds6cH5iqvrsJJJJBR1vjVTa3UJ8ByP1tyUngJxiz3YQY/zX+UtewkO93AkBA4eU4oZvUxi6+HzRBBHqK3AEjWbuGKRRbmyfjLAvp/nNmTo3MKKPRDILkNG2yA9d/WDama9L9wTiS48SGZdIo8JesPfOJ86ag6XnLdC7BoO6s3W7zuTyXJ8Lre3e65oo9zDHqIX1OgAJBSXq9yuYK7aZG0gTgZauKtktJJFRexwmaCGkrUP9c19jrdDBj87A1gr5+dNYPqt0yuZqIw/rNLjXdLUqh9HqBev1abYyfJ4Z3BkgKUyFfyicTUMma8WfGFWlEoKFbwnaL2Z2/d2GhqP876Tu+YpFDC5GcqszBpfy0Gg83nhIsWlaWWSRtkIr0NTL3LewtvZKbIqjb/VM0xeiYf5iBO30Ma9NtiFzYU4D1Z5A5cQzdGlqfF5bN8toL5iCkVs=
|
||||
MYSQL_USER: AgAr+wwear3WFeXjqEuB2bY5SF5KpkGQvEeLZlxKwaMSH+PeVxylHUxlPmg2EQAFezfoR0EzOJtTLttNUDi2+7vLIeS0i6GRoHIHul1oInzMiOQWognohU868OqUwuNJedgwGhskx3TgKspRsUUykhcyQTmPP5GbbfH16i11pYQzCVcZ0H8/rRdey3UDwVZl3nY0Ei6d2kXXEKLwBwRsuQ3q0ZfCYCIrnpSuK00tPYubo16ESYdvy50oP4Ssn2NO2j2Httk6EAUtH1HpAdwmjleEIuVXeXIAdK6Ne5N3JXVTqt/JoI1J0PvPgmIaGSMFTyo59IfKfatD7TqFW/JbDp/Ui3pByqEjNljrrs+RqzJ+6tl77U9/hFO2kN61VP1zLpkK6uNYTnGH1ENjjdmCy3Ih/+j9CJ2DVdQ0pp24cBG1cMi2gm66j/w6389OqAjOQoi7ta8GwVdmltAFij3tvrS+aCV2MSS5BgtqJXk8zfLAPW43MKar2N5aH1N1ODiZ99EIfJM9UsTPRDWDuRPiNzuINF2XxGiG6VQwAVVzmlK6s4R65zU9ZpNVi4gFZZlYhppM2IVlM+02w/7pE1c5Am+tMnpWnIM2Z32YcKASdZT/K1MpL/7CSerfcr+gYh4hepWUFKfirVGoZ4LerIEG0OEDaLM/hxZH3hxh8s/kHRQWovgXqGIkcCLJRlwn/zEyy2WeCnSmcQk=
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-options: Prune=false
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: matomo-secret
|
||||
namespace: solitaire
|
||||
type: Opaque
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: solitaire-secrets
|
||||
namespace: solitaire
|
||||
spec:
|
||||
encryptedData:
|
||||
jwt-secret: AgCDylu761COJ1pbjk1QZbZ9tbwYVX0k2I0T5U/UXhaNPJsNaekiOz0+1063VphPYILey16I+ijc1OplzMgUNaLCdYRCeFjfq1XR2+ntfv3EhtZG19CFlyoZWrQ0guliV3fD0pqMpstClZQ7LmvdeseQ7tknDYrOKg5A19k9eP4kO3SQXW2nuPesDTwddVOs1hU33noIeFAM89cI9TdPC5s9RYjB4sbWFbbtgJPmebku1Mcc/PaTepx0GjaS52OK7cSkcsp+JRlZ6oENfv7EpZSu4V3qW3ajFXLHUSGvxP8VlQGCrLRS2RkNY7b15X3HctYxEdkq1Co4kjArFQ9Ut7qqUl26ANNcfSPnrN3xKA+xBGREr3x8VhQAvWmm/CKAedxmTnIImfpmrerbVELLl3gGz2OztFFzt0l9kcXnlpOBVjmoYvn9O6WStp4YecUy70FrMKmI9Q40qsy10E2hQR5wK+y2ptd6RZ6DCLxS2yRIldqflQPjBdhFjRZl+5cQIwVAaWrqrHd7/J98E1mTd0Pn5cb1iboIdET0Hc/98hF+VMiXQ3GRyih5+brazTePIEpfgDVwmpBodaiqwC2c3qpqWFudt1WQsUupud/koosb3YOWy9sRXTBVaS7hA+X+Pq9woAQeXaJs1CWcPb5amsG7DGpOT8O7DswjODfS53g0yX0SK+6VoZgU0poT68i7PkDyHQ3S96Boz1LxKV8RmLTQbxrREXwXkyCCRof6ZhJfYclKNfncoc8TDRm7rcGfi4lRB2lMq5W1gALJhOl2BADA
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: solitaire-secrets
|
||||
namespace: solitaire
|
||||
type: Opaque
|
||||
@@ -4,6 +4,7 @@ kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- nightscout-configmap.yaml
|
||||
- nightscout-sealedsecret.yaml
|
||||
- mongo-pvc.yaml
|
||||
- mongo-deployment.yaml
|
||||
- mongo-service.yaml
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: nightscout-secret
|
||||
namespace: nightscout
|
||||
spec:
|
||||
encryptedData:
|
||||
API_SECRET: AgC0frB9Yx9u8pzIE1//OVLdEFaFZSAguSlosSF6ZcQg2sJrunqlxygNujo8LxPHv9YsT1Wwjcl7B0+4SVpqygkIAIN15dQiQX8QEYAytGkRv2nnG1q7jaW/m9S5D5EAIyFgzLHtO8zFCw6lO+e5Yt+qgEE7ZRN/eWEOK9LIYmIhVa7Pbc/xQoueqS2XxtHORhyLU3b74e4GEzb0HJDSDYq1LZfTqi1dnuxKTx0zNcFhQL/QM2W+kUhuZEM/eWpdQt4GBgRvNRT1CPUCeHw1Hf5veh38CxiByU3jYy1SA5q/GN5skgq7fEReKGpBLNIadgjt9516Mognt/f+C60E/VHxKyIWb9F6Lf0tJT9lRv2mU9bX3YykPHLzrVIZ/TdFQ/SDqZ6I2Lz5MOSIJWV5pTqPOvMBLNdzGLOx56kdolxBbwNYRAsELKwwlZnW27Kodc1PFgtFbfkwpoVGdnh3xdyFPQTBcUTyZHAMcproZAhMrcALdyT0rAvnNICFetvGsacT0gv+2c88UWvb1UZ0FYKcnQmUWKD5ou69Gspm2mr+izVt1dQPNa5wxLbRFF5pzong4Q/6hVA+VX1i8rR7/D0adaqdvPYOV36Wl80JLmGbgpilFnDTZP5IQV8kaIZqb94Y1YqOlkciAcO6eICQuXf923kigELB9CYPEUoQCOYWCGBFTFxLbIwq3dvQEjgDoNyR0P3D1mPhR6mCEt7n
|
||||
MONGODB_URI: AgA9Wj9k2Tdq3UiZVSK8QBhxqxch4pMWDPzWVG/gRcSsAs0KNGiDfd3MINencnSPF5Qf6ChOdc1XXJVZg83H/no068nNev+qT11T/xs7cFNhFgagYzp5bd7i5vsxIC9f69GfGbtOie32phQtCQgjkjwv9seYFywbR6u6cuaBl0wcU/RJRfLEr+goGW3EEtV0yvqxMUeg574D/uL9TSs2uZ9/XGBWGrEzbwIQkyWpUKSn7BWlCxPLQn1s7yOVQsjCKiehzb5EyEkqXhhv2QKObHyIduETFcx+8Pw40QCLVL4IXaAIV//gxDxPJPRbJ+3kL09sWW1ZdnLyvu6Tf7S228MA7uswq3Qe67q/s3vm31+ujnjGugmFtABRVAwvvmC44r79PtrRatMzeRaqMWAJSb+aGv0vxCBifA2qK/mGMIkniSFK0ciV19+Bv6m4L8IB4B4Vn1ookYjcyp0YiSqTJo2c5gMaCHqfwIS6F53+RBod0iQHByLpELgYx9h7qwoNsbFqFTDQLPk8qH1tBoJO3b1vp6FWIFLlkHbuJLfA8hBnWDEEvKoSbVjiDwyusW6duJ3CF4F0WugtfUtg8YVffR1RllUczzPgie1MCl1eZaY9ro86W1+vMC9SRyewIPTUxi64Xpmv4LtXdqdcBctSw3SxWz0ITjVk2pOgDFDOxYYRpngAiLk+8FGRzKiWM175L1E/LyjQsdsNrwwfhWjkaYQbHyelBBh/R5EJTIx2aqLL7hY6
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-options: Prune=false
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: nightscout-secret
|
||||
namespace: nightscout
|
||||
type: Opaque
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: notesnook-attachments
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: attachments.notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: notesnook-s3
|
||||
port: { number: 9000 }
|
||||
tls:
|
||||
- hosts: ["attachments.notes.aleshym.co"]
|
||||
secretName: notesnook-attachments-tls
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: notesnook-db-init
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PostSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
spec:
|
||||
backoffLimit: 20
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: rs-init
|
||||
image: mongo:7.0.12
|
||||
command: ["bash", "-c"]
|
||||
args:
|
||||
- |
|
||||
until mongosh mongodb://notesnook-db:27017 --quiet --eval 'db.runCommand("ping").ok' ; do
|
||||
echo "waiting for mongod..."; sleep 3;
|
||||
done
|
||||
mongosh mongodb://notesnook-db:27017 --quiet --eval '
|
||||
try { rs.status() }
|
||||
catch (e) { rs.initiate({_id:"rs0", members:[{_id:0, host:"notesnook-db:27017"}]}) }
|
||||
'
|
||||
echo "replica set ready"
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-db
|
||||
namespace: notesnook
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
app: notesnook-db
|
||||
ports:
|
||||
- name: mongo
|
||||
port: 27017
|
||||
targetPort: 27017
|
||||
@@ -0,0 +1,42 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: notesnook-db
|
||||
namespace: notesnook
|
||||
spec:
|
||||
serviceName: notesnook-db
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-db
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-db
|
||||
spec:
|
||||
containers:
|
||||
- name: mongod
|
||||
image: mongo:7.0.12
|
||||
args: ["--replSet", "rs0", "--bind_ip_all"]
|
||||
ports:
|
||||
- containerPort: 27017
|
||||
name: mongo
|
||||
volumeMounts:
|
||||
- name: dbdata
|
||||
mountPath: /data/db
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["mongosh", "--quiet", "--eval", "db.runCommand('ping').ok"]
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
resources:
|
||||
requests: { cpu: "100m", memory: "256Mi" }
|
||||
limits: { cpu: "1000m", memory: "1Gi" }
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: dbdata
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notesnook-identity
|
||||
namespace: notesnook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-identity
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-identity
|
||||
spec:
|
||||
containers:
|
||||
- name: identity
|
||||
image: streetwriters/identity:latest
|
||||
envFrom:
|
||||
- configMapRef: { name: notesnook-config }
|
||||
- secretRef: { name: notesnook-secret }
|
||||
env:
|
||||
- name: MONGODB_CONNECTION_STRING
|
||||
value: "mongodb://notesnook-db:27017/identity?replSet=rs0"
|
||||
- name: MONGODB_DATABASE_NAME
|
||||
value: "identity"
|
||||
ports:
|
||||
- { containerPort: 8264, name: http }
|
||||
readinessProbe:
|
||||
httpGet: { path: /health, port: 8264 }
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 15
|
||||
livenessProbe:
|
||||
httpGet: { path: /health, port: 8264 }
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "500m", memory: "512Mi" }
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: notesnook-auth
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: auth.notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: notesnook-identity
|
||||
port: { name: http }
|
||||
tls:
|
||||
- hosts: ["auth.notes.aleshym.co"]
|
||||
secretName: notesnook-auth-tls
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-identity
|
||||
namespace: notesnook
|
||||
spec:
|
||||
selector:
|
||||
app: notesnook-identity
|
||||
ports:
|
||||
- { name: http, port: 8264, targetPort: 8264 }
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: notesnook
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- notesnook-config.yaml
|
||||
- notesnook-sealedsecret.yaml
|
||||
- db-service.yaml
|
||||
- db-statefulset.yaml
|
||||
- db-init-job.yaml
|
||||
- minio-pvc.yaml
|
||||
- minio-deployment.yaml
|
||||
- minio-service.yaml
|
||||
- minio-bucket-job.yaml
|
||||
- identity-deployment.yaml
|
||||
- identity-service.yaml
|
||||
- identity-ingress.yaml
|
||||
- sync-deployment.yaml
|
||||
- sync-service.yaml
|
||||
- sync-ingress.yaml
|
||||
- sse-deployment.yaml
|
||||
- sse-service.yaml
|
||||
- sse-ingress.yaml
|
||||
- monograph-deployment.yaml
|
||||
- monograph-service.yaml
|
||||
- monograph-ingress.yaml
|
||||
- attachments-ingress.yaml
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: notesnook-s3-setup
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PostSync
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
|
||||
spec:
|
||||
backoffLimit: 20
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: mc
|
||||
image: minio/mc:RELEASE.2024-07-26T13-08-44Z
|
||||
envFrom:
|
||||
- secretRef: { name: notesnook-secret }
|
||||
command: ["/bin/bash", "-c"]
|
||||
args:
|
||||
- |
|
||||
until mc alias set minio http://notesnook-s3:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"; do
|
||||
sleep 2;
|
||||
done
|
||||
mc mb --ignore-existing minio/attachments
|
||||
echo "bucket ready"
|
||||
@@ -0,0 +1,42 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notesnook-s3
|
||||
namespace: notesnook
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy: { type: Recreate }
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-s3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-s3
|
||||
spec:
|
||||
containers:
|
||||
- name: minio
|
||||
image: minio/minio:RELEASE.2024-07-29T22-14-52Z
|
||||
args: ["server", "/data/s3", "--console-address", ":9090"]
|
||||
envFrom:
|
||||
- secretRef: { name: notesnook-secret }
|
||||
env:
|
||||
- name: MINIO_BROWSER
|
||||
value: "on"
|
||||
ports:
|
||||
- { containerPort: 9000, name: api }
|
||||
- { containerPort: 9090, name: console }
|
||||
volumeMounts:
|
||||
- name: s3data
|
||||
mountPath: /data/s3
|
||||
readinessProbe:
|
||||
tcpSocket: { port: 9000 }
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "500m", memory: "512Mi" }
|
||||
volumes:
|
||||
- name: s3data
|
||||
persistentVolumeClaim:
|
||||
claimName: notesnook-s3-data
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: notesnook-s3-data
|
||||
namespace: notesnook
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-s3
|
||||
namespace: notesnook
|
||||
spec:
|
||||
selector:
|
||||
app: notesnook-s3
|
||||
ports:
|
||||
- { name: api, port: 9000, targetPort: 9000 }
|
||||
- { name: console, port: 9090, targetPort: 9090 }
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notesnook-monograph
|
||||
namespace: notesnook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-monograph
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-monograph
|
||||
spec:
|
||||
containers:
|
||||
- name: monograph
|
||||
image: streetwriters/monograph:latest
|
||||
envFrom:
|
||||
- configMapRef: { name: notesnook-config }
|
||||
- secretRef: { name: notesnook-secret }
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: API_HOST
|
||||
value: "http://notesnook-sync:5264"
|
||||
- name: PUBLIC_URL
|
||||
value: "https://monograph.notes.aleshym.co"
|
||||
ports:
|
||||
- { containerPort: 3000, name: http }
|
||||
readinessProbe:
|
||||
httpGet: { path: /, port: 3000 }
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 15
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "500m", memory: "512Mi" }
|
||||
@@ -0,0 +1,21 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: notesnook-monograph
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: monograph.notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service: { name: notesnook-monograph, port: { name: http } }
|
||||
tls:
|
||||
- hosts: ["monograph.notes.aleshym.co"]
|
||||
secretName: notesnook-monograph-tls
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-monograph
|
||||
namespace: notesnook
|
||||
spec:
|
||||
selector:
|
||||
app: notesnook-monograph
|
||||
ports:
|
||||
- { name: http, port: 3000, targetPort: 3000 }
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: notesnook
|
||||
@@ -0,0 +1,29 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: notesnook-config
|
||||
namespace: notesnook
|
||||
data:
|
||||
INSTANCE_NAME: "aleshym-notesnook"
|
||||
DISABLE_SIGNUPS: "true"
|
||||
NOTESNOOK_APP_PUBLIC_URL: "https://app.notesnook.com"
|
||||
AUTH_SERVER_PUBLIC_URL: "https://auth.notes.aleshym.co"
|
||||
MONOGRAPH_PUBLIC_URL: "https://monograph.notes.aleshym.co"
|
||||
ATTACHMENTS_SERVER_PUBLIC_URL: "https://attachments.notes.aleshym.co"
|
||||
NOTESNOOK_CORS_ORIGINS: "https://app.notesnook.com,https://notes.aleshym.co"
|
||||
KNOWN_PROXIES: ""
|
||||
SELF_HOSTED: "1"
|
||||
NOTESNOOK_SERVER_HOST: "notesnook-sync"
|
||||
NOTESNOOK_SERVER_PORT: "5264"
|
||||
IDENTITY_SERVER_HOST: "notesnook-identity"
|
||||
IDENTITY_SERVER_PORT: "8264"
|
||||
SSE_SERVER_HOST: "notesnook-sse"
|
||||
SSE_SERVER_PORT: "7264"
|
||||
IDENTITY_SERVER_URL: "https://auth.notes.aleshym.co"
|
||||
NOTESNOOK_APP_HOST: "https://app.notesnook.com"
|
||||
S3_INTERNAL_SERVICE_URL: "http://notesnook-s3:9000"
|
||||
S3_INTERNAL_BUCKET_NAME: "attachments"
|
||||
S3_SERVICE_URL: "https://attachments.notes.aleshym.co"
|
||||
S3_BUCKET_NAME: "attachments"
|
||||
S3_REGION: "us-east-1"
|
||||
MINIO_BROWSER: "on"
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: notesnook-secret
|
||||
namespace: notesnook
|
||||
spec:
|
||||
encryptedData:
|
||||
MINIO_ROOT_PASSWORD: AgDAj0bx9aNQHIXAcNB3YfA4+P5qIhVNwEDBDDTEcJ9OQ3qrmTrZ0HtYIWswMU1CYuCGzfCT9sdoJ94L4VFWCdqavHdPn3/zDtXXR7WAaMOoYygiKIPTVO1rmRNnk0gzirl1aWSm+fWsc61+SYCvx08C/RnSyTeocfhpY0rvm0r9eiIxmdjc0u2VKksh1ARSkSqJkLniPXBH6PCFH/tUOH3FZqYycCW+FEhj8KE9D/FIVKy3pCFhfg9KfH7XHqQx5wnQ0j2aL9ljqA6DPI6tDB5rcR8lW7+3iC8X3YZwaExFfWq+3ksZj5LLnESBlqwvV1IY8B7k1VoiL8f5r/5VAE/EJ6SFvIueoOnKxUWC+NJroWeBSnk0isDpNPluVkrGJOVTHv0bx3bAhUVjr3lQsimvyi1ZZXKvr/b/Hpg++RLTF3wBGLjM7tsJ0RHnzysvIyfg8euU2OYitjfkQKbvDIREkU5A0AQ35oapgNnVAB2gWlm4SAkkfOQuT/oowTOLEYKg4zzk64lQiOWFtD5q82Dr6PU6s/snIDixkY/5ICgcV5/Y1Oq0wtvhblDBroWpEnohoQAqlYkRNN4jh+oSEJwJORv6mF370XxBxY9k3h/IyCdJeC9xv9DocUjUP+FU9HQE7Qvd3OcNag1DAyhmtLv5wDgZEea3fPX5XMhnvE5U+YUxZDXkN1MchItTTMMim5fySIPg5KZ+Hr2Ticpxfjw47RMFNH3y2qfDqb8hBfB9SA==
|
||||
MINIO_ROOT_USER: AgAyLNT8gHmThqLjQBBcmoZsxtEG/ZR/hjUICEZKAyF8+FV6A3WFizA8tYBzjNyfaGFSdS2yLmZGbwtEKmHSApBmXoY4xgnyZpEjh2whsgeBjMFdx2jWt+odvEaPGA+xEbuODJbkJMQiXhi/ctyiMD1/y6WIWZ5Ya8Ahz469di0fT1znMk9B050FKFgVEK6Sfzb8MQuZBj2wukTgZ2rlKMQ+7EQi6pb6cEATLDjheXvbgx4g9LTPwVN+1uZcC5GU7jg/IGJcEg/QJfmOCgRmz9iDx3/d9lFnDnJBOv8Plt6PmWEMR9QOvdQjUhWiN2BwrdICHqFxFTnV+z9JbMk2kT2Ovw6W239BT+KbabEaiwVE5+3ubLLymF3bXwRO7fqPO3xy6/aVsv6k9995jPY7/DNIr5kozo/uFf2DeP8sbldJp2ymR2RBoa8TCiGHT5pgWr36qHxMt1J7fU1mWIRPeUmkFJchCjNc8qDr1bwY9hjrdXxC5fUkFsG/gyb5FsmzvQaAPAy45Wl1DRpp4xBiE/SUc+xBcA1y3Bo6lJ2w7w8tB2bMZUqZkgOyPDaPfzLViMtPUlnCnKRGzvi8HoGvzLS4gN1jjRUK33YtLpLqa6B9Wj1/fkgu9H1cIz1ffS3baL4ZIyA3K7/4LrAS2xzUcJ8HdezxjuZQKqtwDgJSysZJ0blXG6GLpx8JonZh4gx8na1c9kKt07BNYao=
|
||||
NOTESNOOK_API_SECRET: AgC3JoH/PCMgUPPvUCkIsRWGEIJtwt+E+wXvfBTi2V60nN19RiJLpXF860UrKnFZxvloFalJLIxA1mtLUAfO2wZuzGpid8IIrAUIyhXCZkZWkZr9UbB/TjzmorC5NEAwjo08zhBKIlME+Emdb7BrpHGbj2ykQlyKqwCSTPKoitkypdgXQsbPWM6j03o/F9hSmKVFCTb6ugBY72rXBlVBqBGdeoPLkwSnuk7kk6y+lOfufeqGw2MXoNCZJ1+rbZQ2ihGFSGLKd955I3wnidcu8oCQO50u4V1IHhj0/iJHh/bXu6ipXEw4ZX886SFQgqqb36sFt8b2PIk+h2GtqFC+Ixew+ay790B/wXIMWhze680oAwq/6ZvWe0PSbJgDXLCYga7ZIw8RYFphk3+VblYfc9EObGWvuLDoGzSs/qgi/+euxOkM1P8lGUGuSETz4Bm7VEg4udEneuCnfHqQ19/qkhu/isMwN7KpOaymKwpw0Yb2oefUljjPOGHq+ni08TUU1V+fmKMmnxG3Uq05mLDjTpN6lkpm5yF5xCA4WRflL1kqLFsCPm4JF62NiLDXuVb7c0uz9l85YaJg06RaYVHOf5Km7B3wnBHHrk8sJ2NtkUa2vvGbkZ64JFP9PrD7Ubvh62i4QoXn83nv67rihtzB8BNzkA7sQVLH2OpBqFkbJaTquBWXo3zg8AWcxpuXvNXv6ba4kZJEbBbCDpQDUdQ//MSJttHEgPKvZgmqtqCQ7W3Mypr6CWWGhSfcEmgAzcSfYlYNAbGAGxFErDkXelhbsgNr
|
||||
S3_ACCESS_KEY: AgC9kcAVZEpanlIjfkpJjDDJe6nikUTorye4l03NhgOoMrU6ivxVXBboiXAYyRYYRidp60r041YPs5lZNCYOwwpuf5iquAlAuD5mecR2xFJIsv3Mxi5wEdoFvPtCIFv8Xm0MTMvR1ch4J4X1gds4S3HQmYIxEKSg/qU+/4ww1CccbDexGOhwOwMMor2KZwfGTrDIZO7/3SwMQs6h8XVRUR5BhuLGftV6tUFEOQBQ6pjijurkSW/faG6YJbE2hpmWNdot6Qau7matCcJhRZI0NAeZhAPeCvIlyrEyJpqjMxNEMVJszj//F7dlPxTW8AkWyw2bmzzgNPhAYvpRTxrDDr70huRUWIo5mAlBbJypT3haVjKYFoRKAGXWC/8dhLSp3NKWQ/smBzpvUOQ30N4EskIVT+V8GArkrLN35yi2yP1aFaQpvLCA1m8pp5hUTgyDm1dqcOnQg3DRJgiMN7/Jt+P9TQGs2RiKoHCPp6lIfDGRKDLYeG5uuiUouridf1HnkrKGXlSmnd4xYWYlym6ZAC7Uzd9GlwTeZwZT8YJCQHqnZFPGq6MAVOPR5B+kQldAH81qosXjRdQyn8RT3DefdAjbduh4/DzuLJTTu8OeSmyPx7H5uqDIrkn1DLuBca2+lJPu4xMv6bthGRzwBLA1aU+nmniFBznkg+YlI5XxJLrA5Ra/PZsglhCHRz1hSiajZ5g1jUzMk2XDjti/q3Xbqj1AkmLmKjZbR05e7VRELMIj4g==
|
||||
S3_ACCESS_KEY_ID: AgAjLq/HHmD30h2/2mHU/iQKS6l4PRTRhquB4UgCdJjkr/a4eGfxxPZWRJJrj/AbFaWQrAq/ADqa9cAxwXsJInwVLkmv42sB9hODGxRtKbQgwubJaa84uOqdNvce/GvlxXrVIh5H7GUTTKuIXSuS5U/AtBniPudJ5/p8QulYXyqY3K34uePIMEqBP5m7CJ8kKB7EPTLvb29pjr/VdKCpSMwnhXiiiwU/Y1NDdf4ZiIc1gsp79kRd1RhFsLfJky/tBntv4waHHfYKZ7jkCJOGp189ZSqNFlAkP2TPg+jpygpznPvTTggQB45DlYKupUI1bMGlkWcfYLM9+/jmvHngGuzPvSUlGFukaPHkhJ8YwLz+Jc+K6xxdbX9WjzqqGYs3CW41ccxHjxgrZnUW62NbCYIR7xj1K2vCVmmyjwC++N9ArDvS/VreXAq2QJsxKQjL7YElsQrFlMzKrRw98kUd4vpQPQxK+nkLe9HVj0VJprDK22ZrfCcyA8rl64Zx8huwbKEmNscZou49oDx3LJRI8vGFS0lQJFLBrjJKjF9xT+7R59SIo+7JxFAWDvmEaJrdPSq4EDEturHhzvKcv5y1Gy1ul4nwIGvxDZAK9kwp5oiGxfARncoVzgFINqwlVmVfPRb4RZl5mzu8IToMebwvJ52Rpf+IhkW1yEnCYxppBNmUyPWewTc3UUvtViOOawaARaXii6O9sR0uhlE=
|
||||
SMTP_HOST: AgBZiWzF45lbMOrRXlGi2k1jq2bA+hQWF1DVLJemKgn9et0uBfcIyICHS/7E4cukDI+MOInEQEYem5M4FoQ2mM2ylcIm+LrUI+p5azDKRYkkbcwKD2F0+J9G5YDXxYth11NzS3fLhrVxAuqmbqy3LxOmSkjFvFdJxLx0Su78B2CSDBXmy+gu4soY7bdQV0L3T/zfG6s1WuLzR8WPnji3EmBM5dexG7Smhziz0dymOuhdGfSwaI2MWrYzw9J3Vshc+mRXzw1JvI5mVbawhQOLrnW57NvXlJtk19LLr/kXWOt1ppJcJOYGOxWrFs4VA4Bg4QSN2piARWZ6FbL1/a8ynamUhw+R2yDlT/J8oQIVBzhIWCU5EwfHgDu+QRKzmd2iAhfYZjrUkJNojUjqaQvb7RXrG4DVL9UJxGjushebgc2EG/PykdzoRL2d1KMfJRK6O5ouM42UvOCqRzbBbWuTiA6fj8hGTIZYFXX3vWIM368uDvrEVxj2C+skrRzHEzAQgsJEaWtFvZm6WGkZZOLTuava9+JxMvnpftWMIE8FrYCAYwn5xa88eftgw+GW65EWVSm8IXFtGoNd4DKYj5OGF2DcN//OxteoVd3OxsmuJtY0Qx0REb9nqg9Qdw2bP6emmpmgtyw8opUS/VubGghdCnhLUES+CA08ooK4a8n8rZSSFiujvjMjoDdOnVktM/D3R6ECftJRakhMes4INANM4w==
|
||||
SMTP_PASSWORD: AgAdjxnT2uC0LRUmlQ/BOlLs6l1PCP13tMmgmSnqqNWSuucYkWwTWtz4qBnG3dauhC0ufS5eqABJqZq0r+QpyhxU0Otqzk7oVcea0Mq9ai159IMCx/rZL8HXIkUF76muv8GOGDYlaf3A+CbrNTsaRXDbjZ7LjzD8OwsdfaeIzUejyNXgs5EMjSOHLx4YhmVfVEZLAtoP9zcNlDSNFaat79wnwjwUifHuY3usvQXhJS+vLVtLLtWwKF0rCsfkbCRwJtJCCrOvOLuid8aZ3cO9Y7n0+TOAlkSe4dSSq0D2psg3njEZ2l3RNLW2YbM2LkcXK+MjIpCivqSCXLufq1dUuhDxCirxSd0VgTt5TPgeTCi+kuNv/UAqydQWDQlh6izUDdnTtMlRqtk4ZFhoBvIo1NVdpObmbFlSlBURwrmNqmdyO1/MNZT40gi1UsuEtktDCohoq6zQrahiGqcXr1VVscE+u5VoV7PRTICMSJQexFV9fx8YsZxDycng7zaf6xbK0JWVSuU1IXwb1ZOReQs0MtZj45o/c9JtYS1HPoSZRCCSCAd6qPhnHoUc6jYYFznfKTqvrpSdXqoBF61I1Sw+CKuVA/aut9mgCzWIwGjb38Hjh/VYufrgkFMcvnqbpkX4F8sMvzI3zsvFT2S0RwNGHOay6f+otg2KJdPlE2HSLVGLTFxS1Ku2F3ZPL79/Br8eCW3PPnq4c6w4f0XBFtuGSXtt
|
||||
SMTP_PORT: AgATegLz9D+74mUBak9tv+7L/LeRqcJXcE58T1fOh1shsz3KjSzuXb4HxytqghT0LqplAEemCPoG823wPFKYnxZtvedQ2CyZGCxEXPoV6iUvBAVC3Pj6H92k9CIEZRPHLvR6IR+mXzu6HLWwJsOGrxn28waSx374uVizylptkpjIHvyseY3chfH0N/x2tK0yZn9rKOqnzfjKVGh4DF+i7wU/uuFPhcMbqRPg70J0pxhesTKHBAerGnvfnoCxFRoSl0ZPgC0kK5NfIEp3zWW45NWshPNsbb0yix76Rnr+8dEn/Rbopa8jZtfdK260ZPclLRO6z04ks69Zp5u9kE1g883Q25I+M9C8B27QDC+KHxgSwq0w8a5CwlCYDRu/e9UBHtsJobRaARgghyyAVsS4FsDS45kJom3U7n0Ip/27hg2VMm+jwNRoB5cmlyhDDlFng49U36DRsd0z2d2wBFAG4NM4xgbWlK5zUDmKCveC7zJfFAuZGsMbY5U8/lDv1WvGIG8xmQsg5CgriVL6gG3ReRjk3BmK93b8OkUhfquFHKBoyqJZWIIz+W5dcP2fKdjZc0Ec2vPqszHaNTN+dEcGzm9MGFpeus6rA6Egxzq1esVbEPuixcI6eXc87SOG0Ntn2wbPJQKpNZq+JkzYSXBUZcemPz5M2nybLr/lwuX7//Qg5b95nwzXMRRm9kagwWubDPNPpd8=
|
||||
SMTP_USERNAME: AgAxdxvMjUjvj8vYu8upKMfpAIi00dBizXD83F5f6TCSlAFlo3kuVFbfJ8tCQZNRSdU7vP6n3zQfW2IhYUQSnO44+Wb8q5V4sfs1cClbPyGpQdEyFeLk1KwW3LeFxsi9cym3H7Qtu9e/S3dOmD9IxK8AuOJkq5t7Xepum7WvuOuvBVFm2nVetZlTs1BmHoxA8dHlAhk+qwGMAuo/Xltut67uDsPzZTcA4fEAYe+We7zqfD0hpN+k1TffZHE7x+GpA3tyc6yUf/uSsWRq9HKyASc0UM5uoK/WoxYoUA7RF5j5FXFrA1jY/Y1pE3oLjQmpB59q5vOasd77uXE/1b+rs6l8L1zngWw+8MeKA/julrmWBwte+vs68yqDFOL7QNfwpYmYoaAcuvezeXZwF4nnwzXyE8ZEO7xtbp/r9DATeGypjRnm1bDAMTARGLgHoxV18UELCiksNNCbVO7twXJs+HUE/sbfI3tQmk6O5zbWD/GbcR0lTsTjM+hcM5r9ZYfrq7Ad3Oyb3ANU184Rm4fiR9v+t9OyJf9jIASF9p8oFcTr2dupdv3r8wzPq/ayfvN44nyOr2xVfIv+ubMqjjLixg0jUDeFoR/GST6fpvWBmd2HVMG1tHvZxphUb0V/M946HxKBMpsFn/WWyLgUzJG64bIICDAetCferbaa4UFEHP23G/R5tLBWM2GNZpT6yfkBQYppc6e1/KAjGNVMrTpKQeNkD/xs
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
sealedsecrets.bitnami.com/managed: "true"
|
||||
name: notesnook-secret
|
||||
namespace: notesnook
|
||||
type: Opaque
|
||||
@@ -0,0 +1,25 @@
|
||||
# NOTE: This app's Secret is now managed via SealedSecrets (notesnook-sealedsecret.yaml),
|
||||
# which IS committed (encrypted). You should not need to hand-apply a plain Secret.
|
||||
#
|
||||
# This file remains only as documentation of the required keys.
|
||||
#
|
||||
# To change a secret value:
|
||||
# 1. Build a plain Secret locally (NEVER commit it):
|
||||
# kubectl -n notesnook create secret generic notesnook-secret \
|
||||
# --from-literal=KEY=value ... --dry-run=client -o yaml > /tmp/s.yaml
|
||||
# 2. Seal it:
|
||||
# kubeseal --format yaml --scope strict < /tmp/s.yaml \
|
||||
# > apps/notesnook/notesnook-sealedsecret.yaml
|
||||
# 3. Commit the SealedSecret; ArgoCD applies it, controller unseals it.
|
||||
#
|
||||
# DISASTER RECOVERY: the controller's private key is backed up GPG-encrypted at
|
||||
# /mnt/bulk/backups/sealed-secrets/master-key-YYYYMMDD.gpg (passphrase in password manager)
|
||||
# Restore it BEFORE applying SealedSecrets to a rebuilt cluster:
|
||||
# gpg -d master-key-YYYYMMDD.gpg | kubectl apply -f -
|
||||
# kubectl -n kube-system rollout restart deploy/sealed-secrets-controller
|
||||
#
|
||||
# Required keys:
|
||||
# NOTESNOOK_API_SECRET (reuse existing — do NOT regenerate)
|
||||
# SMTP_USERNAME, SMTP_PASSWORD, SMTP_HOST, SMTP_PORT
|
||||
# MINIO_ROOT_USER, MINIO_ROOT_PASSWORD
|
||||
# S3_ACCESS_KEY_ID (= MINIO_ROOT_USER), S3_ACCESS_KEY (= MINIO_ROOT_PASSWORD)
|
||||
@@ -0,0 +1,34 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notesnook-sse
|
||||
namespace: notesnook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-sse
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-sse
|
||||
spec:
|
||||
containers:
|
||||
- name: sse
|
||||
image: streetwriters/sse:latest
|
||||
envFrom:
|
||||
- configMapRef: { name: notesnook-config }
|
||||
- secretRef: { name: notesnook-secret }
|
||||
ports:
|
||||
- { containerPort: 7264, name: http }
|
||||
readinessProbe:
|
||||
httpGet: { path: /health, port: 7264 }
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 15
|
||||
livenessProbe:
|
||||
httpGet: { path: /health, port: 7264 }
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "500m", memory: "256Mi" }
|
||||
@@ -0,0 +1,21 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: notesnook-sse
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: events.notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service: { name: notesnook-sse, port: { name: http } }
|
||||
tls:
|
||||
- hosts: ["events.notes.aleshym.co"]
|
||||
secretName: notesnook-sse-tls
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-sse
|
||||
namespace: notesnook
|
||||
spec:
|
||||
selector:
|
||||
app: notesnook-sse
|
||||
ports:
|
||||
- { name: http, port: 7264, targetPort: 7264 }
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notesnook-sync
|
||||
namespace: notesnook
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notesnook-sync
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notesnook-sync
|
||||
spec:
|
||||
containers:
|
||||
- name: sync
|
||||
image: streetwriters/notesnook-sync:latest
|
||||
envFrom:
|
||||
- configMapRef: { name: notesnook-config }
|
||||
- secretRef: { name: notesnook-secret }
|
||||
env:
|
||||
- name: MONGODB_CONNECTION_STRING
|
||||
value: "mongodb://notesnook-db:27017/?replSet=rs0"
|
||||
- name: MONGODB_DATABASE_NAME
|
||||
value: "notesnook"
|
||||
ports:
|
||||
- { containerPort: 5264, name: http }
|
||||
readinessProbe:
|
||||
httpGet: { path: /health, port: 5264 }
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 15
|
||||
livenessProbe:
|
||||
httpGet: { path: /health, port: 5264 }
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "128Mi" }
|
||||
limits: { cpu: "1000m", memory: "512Mi" }
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: notesnook-sync
|
||||
namespace: notesnook
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: sync.notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service: { name: notesnook-sync, port: { name: http } }
|
||||
- host: notes.aleshym.co
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service: { name: notesnook-sync, port: { name: http } }
|
||||
tls:
|
||||
- hosts: ["sync.notes.aleshym.co", "notes.aleshym.co"]
|
||||
secretName: notesnook-sync-tls
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notesnook-sync
|
||||
namespace: notesnook
|
||||
spec:
|
||||
selector:
|
||||
app: notesnook-sync
|
||||
ports:
|
||||
- { name: http, port: 5264, targetPort: 5264 }
|
||||
@@ -0,0 +1,26 @@
|
||||
# sealed-secrets
|
||||
|
||||
Controller v0.38.4, applied directly (not via ArgoCD — matches how cert-manager is handled;
|
||||
ArgoCD apps here are workloads, not cluster infra):
|
||||
|
||||
kubectl apply -f apps/sealed-secrets/controller.yaml
|
||||
|
||||
`controller.yaml` is the upstream release manifest plus ONE local change:
|
||||
`--key-renew-period=0`. Upstream defaults to rotating the sealing key every 30 days;
|
||||
kubeseal always seals with the newest key, so a rotation would silently invalidate the
|
||||
off-cluster key backup for anything sealed afterwards. Pinning to one key keeps the
|
||||
backup permanently valid. Rotation buys little here — it never re-seals existing secrets.
|
||||
|
||||
## Disaster recovery (order matters)
|
||||
|
||||
# 1. install controller
|
||||
kubectl apply -f apps/sealed-secrets/controller.yaml
|
||||
# 2. restore the private key BEFORE applying any SealedSecret,
|
||||
# or a fresh key is generated and nothing decrypts
|
||||
gpg -d /mnt/bulk/backups/sealed-secrets/master-key-YYYYMMDD.gpg | kubectl apply -f -
|
||||
kubectl -n kube-system rollout restart deploy/sealed-secrets-controller
|
||||
# 3. apply the SealedSecrets
|
||||
kubectl apply -f apps/cluster-secrets/ # cert-manager, argocd, solitaire
|
||||
# (apps/notesnook + apps/nightscout SealedSecrets come via ArgoCD)
|
||||
|
||||
Passphrase for the .gpg is in the password manager, and nowhere else.
|
||||
@@ -0,0 +1,415 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
spec:
|
||||
minReadySeconds: 30
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
name: sealed-secrets-controller
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 25%
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
spec:
|
||||
containers:
|
||||
- args: []
|
||||
command:
|
||||
- controller
|
||||
env: []
|
||||
args:
|
||||
- --key-renew-period=0 # renewal disabled: keeps ONE key so the GPG key backup stays valid forever
|
||||
image: docker.io/bitnami/sealed-secrets-controller:0.38.4
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
name: sealed-secrets-controller
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
- containerPort: 8081
|
||||
name: metrics
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
stdin: false
|
||||
tty: false
|
||||
volumeMounts:
|
||||
- mountPath: /tmp
|
||||
name: tmp
|
||||
imagePullSecrets: []
|
||||
initContainers: []
|
||||
securityContext:
|
||||
fsGroup: 65534
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
serviceAccountName: sealed-secrets-controller
|
||||
terminationGracePeriodSeconds: 30
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: tmp
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: sealedsecrets.bitnami.com
|
||||
spec:
|
||||
group: bitnami.com
|
||||
names:
|
||||
kind: SealedSecret
|
||||
listKind: SealedSecretList
|
||||
plural: sealedsecrets
|
||||
singular: sealedsecret
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
SealedSecret is the K8s representation of a "sealed Secret" - a
|
||||
regular k8s Secret that has been sealed (encrypted) using the
|
||||
controller's key.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: SealedSecretSpec is the specification of a SealedSecret.
|
||||
properties:
|
||||
data:
|
||||
description: Data is deprecated and will be removed eventually. Use
|
||||
per-value EncryptedData instead.
|
||||
format: byte
|
||||
type: string
|
||||
encryptedData:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
template:
|
||||
description: |-
|
||||
Template defines the structure of the Secret that will be
|
||||
created from this sealed secret.
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Keys that should be templated using decrypted data.
|
||||
nullable: true
|
||||
type: object
|
||||
immutable:
|
||||
description: |-
|
||||
Immutable, if set to true, ensures that data stored in the Secret cannot
|
||||
be updated (only object metadata can be modified).
|
||||
If not set to true, the field can be modified at any time.
|
||||
Defaulted to nil.
|
||||
type: boolean
|
||||
metadata:
|
||||
description: |-
|
||||
Standard object's metadata.
|
||||
More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
|
||||
nullable: true
|
||||
properties:
|
||||
annotations:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
finalizers:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
type:
|
||||
description: Used to facilitate programmatic handling of secret
|
||||
data.
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- encryptedData
|
||||
type: object
|
||||
status:
|
||||
description: SealedSecretStatus is the most recently observed status of
|
||||
the SealedSecret.
|
||||
properties:
|
||||
conditions:
|
||||
description: Represents the latest available observations of a sealed
|
||||
secret's current state.
|
||||
items:
|
||||
description: SealedSecretCondition describes the state of a sealed
|
||||
secret at a certain point.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: Last time the condition transitioned from one status
|
||||
to another.
|
||||
format: date-time
|
||||
type: string
|
||||
lastUpdateTime:
|
||||
description: The last time this condition was updated.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A human readable message indicating details about
|
||||
the transition.
|
||||
type: string
|
||||
reason:
|
||||
description: The reason for the condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: |-
|
||||
Status of the condition for a sealed secret.
|
||||
Valid values for "Synced": "True", "False", or "Unknown".
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
Type of condition for a sealed secret.
|
||||
Valid value: "Synced"
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
observedGeneration:
|
||||
description: ObservedGeneration reflects the generation most recently
|
||||
observed by the sealed-secrets controller.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
selector:
|
||||
name: sealed-secrets-controller
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-service-proxier
|
||||
name: sealed-secrets-service-proxier
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- sealed-secrets-controller
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resourceNames:
|
||||
- 'http:sealed-secrets-controller:'
|
||||
- http:sealed-secrets-controller:http
|
||||
- sealed-secrets-controller
|
||||
resources:
|
||||
- services/proxy
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller-metrics
|
||||
name: sealed-secrets-controller-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ports:
|
||||
- port: 8081
|
||||
targetPort: 8081
|
||||
selector:
|
||||
name: sealed-secrets-controller
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-service-proxier
|
||||
name: sealed-secrets-service-proxier
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: sealed-secrets-service-proxier
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:authenticated
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: sealed-secrets-key-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-key-admin
|
||||
name: sealed-secrets-key-admin
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- list
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: sealed-secrets-controller
|
||||
name: sealed-secrets-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: secrets-unsealer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: sealed-secrets-controller
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
name: secrets-unsealer
|
||||
name: secrets-unsealer
|
||||
rules:
|
||||
- apiGroups:
|
||||
- bitnami.com
|
||||
resources:
|
||||
- sealedsecrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- bitnami.com
|
||||
resources:
|
||||
- sealedsecrets/status
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- get
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: notesnook
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://git.aleshym.co/funman300/k3s-homelab.git
|
||||
targetRevision: main
|
||||
path: apps/notesnook
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: notesnook
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,222 @@
|
||||
# Notesnook self-host → k3s migration — design
|
||||
|
||||
**Date:** 2026-07-15
|
||||
**Status:** Approved design, pending spec review → implementation plan
|
||||
|
||||
## Goal
|
||||
|
||||
Move the self-hosted Notesnook stack from the docker-compose deployment on host
|
||||
`10.10.0.5` (LXC on Proxmox `pve`) into the k3s cluster (CT 114, node IP
|
||||
`10.10.0.100`), managed by ArgoCD via the `k3s-homelab` GitOps repo. The existing
|
||||
account, notes, encryption keys, and attachments must be preserved.
|
||||
|
||||
## Current state (source)
|
||||
|
||||
- **Host:** `manage@10.10.0.5`, docker-compose stack in `~/notesnook`.
|
||||
- **Services:** MongoDB 7.0.12 (replica set `rs0`), MinIO (S3), identity/auth,
|
||||
sync, sse/events, monograph, plus bootstrap helpers (`validate`, `setup-s3`)
|
||||
and `autoheal`.
|
||||
- **Public hosts** (Traefik on the compose box, cert via Cloudflare resolver):
|
||||
`auth`, `sync` (+ apex `notes`), `events`, `monograph`, `attachments`,
|
||||
`minio` — all under `notes.aleshym.co`.
|
||||
- **Data lives in:** Mongo databases `identity` and `notesnook`; MinIO bucket
|
||||
`attachments`.
|
||||
- **Root cause history:** login-code email was failing because SMTP was left at
|
||||
`CHANGE_ME`; now fixed to Gmail SMTP. That corrected config carries into the
|
||||
migration.
|
||||
|
||||
## Target cluster conventions (observed)
|
||||
|
||||
- **GitOps:** ArgoCD (no app-of-apps). Monorepo
|
||||
`https://git.aleshym.co/funman300/k3s-homelab.git`, branch `main`. App
|
||||
manifests under `apps/<name>/` (kustomize); the `Application` CR committed at
|
||||
`argocd/<name>.yaml` with `automated {prune, selfHeal}` + `CreateNamespace=true`.
|
||||
- **Ingress:** Traefik (`ingressClassName: traefik`), entrypoint `websecure`.
|
||||
- **TLS:** cert-manager clusterissuer `letsencrypt-prod` via **Cloudflare DNS-01**.
|
||||
Per-host TLS secret named `<something>-tls`.
|
||||
- **Storage:** single-node `local-path` (default), `ReadWriteOnce`, `Delete`
|
||||
reclaim. **Implication:** deleting a PVC deletes its data — treat Mongo/MinIO
|
||||
PVCs as precious.
|
||||
- **Secrets:** no sealed-secrets / external-secrets controller. Convention is a
|
||||
committed `*-secret.yaml.example`; the real `*-secret.yaml` is **gitignored**
|
||||
(`apps/**/*-secret.yaml`) and applied once with `kubectl apply`. The
|
||||
`Application` CR uses `ignoreDifferences` on the Secret's `/data` so ArgoCD
|
||||
won't fight the out-of-band secret.
|
||||
- **Internal DNS:** resolver `10.10.0.8` holds split-horizon `*.aleshym.co` → LAN
|
||||
IP records (`ns.aleshym.co → 10.10.0.100`, `auth.notes.aleshym.co → 10.10.0.5`
|
||||
today). This is where the cutover repoint happens. Public authority for
|
||||
`aleshym.co` is Cloudflare (used only for the ACME DNS-01 challenge).
|
||||
|
||||
## Architecture
|
||||
|
||||
Namespace: `notesnook`. Repo path: `apps/notesnook/`. ArgoCD app:
|
||||
`argocd/notesnook.yaml`.
|
||||
|
||||
| Component | Image | k8s workload | Service (in-cluster) | Public ingress host → port |
|
||||
|---|---|---|---|---|
|
||||
| MongoDB `rs0` | `mongo:7.0.12` | StatefulSet (1) + headless Service + init Job | `notesnook-db:27017` | none |
|
||||
| MinIO | `minio/minio:RELEASE.2024-07-29T22-14-52Z` | Deployment + PVC | `notesnook-s3:9000` | `attachments.notes.aleshym.co` → 9000 |
|
||||
| Identity/auth | `streetwriters/identity:latest` | Deployment | `notesnook-identity:8264` | `auth.notes.aleshym.co` → 8264 |
|
||||
| Sync | `streetwriters/notesnook-sync:latest` | Deployment | `notesnook-sync:5264` | `sync.notes.aleshym.co` + `notes.aleshym.co` → 5264 |
|
||||
| SSE/events | `streetwriters/sse:latest` | Deployment | `notesnook-sse:7264` | `events.notes.aleshym.co` → 7264 |
|
||||
| Monograph | `streetwriters/monograph:latest` | Deployment | `notesnook-monograph:3000` | `monograph.notes.aleshym.co` → 3000 |
|
||||
|
||||
**Dropped from compose:** `autoheal` (replaced by k8s liveness/readiness probes),
|
||||
`validate` (env presence enforced by manifests), `setup-s3` (folded into a MinIO
|
||||
bucket-create init Job / or `mc` init container).
|
||||
|
||||
### MongoDB replica set (the one real deviation from the nightscout template)
|
||||
|
||||
Notesnook's connection strings use `?replSet=rs0` and the sync server relies on
|
||||
change streams/transactions, which require a replica set. Plan:
|
||||
|
||||
- **StatefulSet** (1 replica) `notesnook-db`, `command: [mongod, --replSet, rs0, --bind_ip_all]`.
|
||||
- **Headless Service** `notesnook-db` for stable DNS.
|
||||
- **One-time init Job** (or `postStart`) runs
|
||||
`rs.initiate({_id:"rs0", members:[{_id:0, host:"notesnook-db:27017"}]})`,
|
||||
idempotent (skip if already initialized). The RS member host must be the stable
|
||||
Service name `notesnook-db:27017` so clients using `?replSet=rs0` resolve the
|
||||
primary correctly.
|
||||
- No Mongo auth (matches current compose: `mongodb://notesnook-db:27017`, no
|
||||
credentials). Access is namespace-internal only.
|
||||
|
||||
### Config vs secrets
|
||||
|
||||
**ConfigMap `notesnook-config`** (committed, non-secret):
|
||||
|
||||
```
|
||||
INSTANCE_NAME=aleshym-notesnook
|
||||
DISABLE_SIGNUPS=true
|
||||
NOTESNOOK_APP_PUBLIC_URL=https://app.notesnook.com
|
||||
AUTH_SERVER_PUBLIC_URL=https://auth.notes.aleshym.co
|
||||
MONOGRAPH_PUBLIC_URL=https://monograph.notes.aleshym.co
|
||||
ATTACHMENTS_SERVER_PUBLIC_URL=https://attachments.notes.aleshym.co
|
||||
NOTESNOOK_CORS_ORIGINS=https://app.notesnook.com,https://notes.aleshym.co
|
||||
KNOWN_PROXIES= # may need the Traefik pod CIDR; see open items
|
||||
# service-discovery (in-cluster names replace compose hostnames)
|
||||
NOTESNOOK_SERVER_HOST=notesnook-sync
|
||||
NOTESNOOK_SERVER_PORT=5264
|
||||
IDENTITY_SERVER_HOST=notesnook-identity
|
||||
IDENTITY_SERVER_PORT=8264
|
||||
SSE_SERVER_HOST=notesnook-sse
|
||||
SSE_SERVER_PORT=7264
|
||||
IDENTITY_SERVER_URL=https://auth.notes.aleshym.co
|
||||
NOTESNOOK_APP_HOST=https://app.notesnook.com
|
||||
# S3/MinIO non-secret
|
||||
S3_INTERNAL_SERVICE_URL=http://notesnook-s3:9000
|
||||
S3_INTERNAL_BUCKET_NAME=attachments
|
||||
S3_SERVICE_URL=https://attachments.notes.aleshym.co
|
||||
S3_BUCKET_NAME=attachments
|
||||
S3_REGION=us-east-1
|
||||
MONGODB_CONNECTION_STRING (identity)=mongodb://notesnook-db:27017/identity?replSet=rs0
|
||||
MONGODB_CONNECTION_STRING (sync)=mongodb://notesnook-db:27017/?replSet=rs0
|
||||
```
|
||||
|
||||
**Secret `notesnook-secret`** (out-of-band, `*.example` committed, real gitignored):
|
||||
|
||||
```
|
||||
NOTESNOOK_API_SECRET=... # reuse existing value from old .env
|
||||
SMTP_USERNAME=funman300@gmail.com
|
||||
SMTP_PASSWORD=... # Gmail app password
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
MINIO_ROOT_USER=notesnook
|
||||
MINIO_ROOT_PASSWORD=... # reuse existing value
|
||||
S3_ACCESS_KEY_ID=notesnook # = MINIO_ROOT_USER
|
||||
S3_ACCESS_KEY=... # = MINIO_ROOT_PASSWORD
|
||||
# TWILIO_* : present in old .env but SMS 2FA unused; omit unless in use
|
||||
```
|
||||
|
||||
Reusing `NOTESNOOK_API_SECRET`, MinIO creds, and the Mongo data verbatim is what
|
||||
keeps the migrated account valid — do **not** regenerate the API secret.
|
||||
|
||||
## Repo layout to add
|
||||
|
||||
```
|
||||
apps/notesnook/
|
||||
kustomization.yaml
|
||||
namespace.yaml
|
||||
notesnook-config.yaml # ConfigMap
|
||||
notesnook-secret.yaml.example # secret template (real applied out-of-band)
|
||||
db-statefulset.yaml # mongo rs0
|
||||
db-service.yaml # headless
|
||||
db-init-job.yaml # rs.initiate (idempotent)
|
||||
minio-pvc.yaml
|
||||
minio-deployment.yaml
|
||||
minio-service.yaml
|
||||
minio-bucket-job.yaml # create 'attachments' bucket
|
||||
identity-deployment.yaml + identity-service.yaml + identity-ingress.yaml
|
||||
sync-deployment.yaml + sync-service.yaml + sync-ingress.yaml
|
||||
sse-deployment.yaml + sse-service.yaml + sse-ingress.yaml
|
||||
monograph-deployment.yaml+ monograph-service.yaml+ monograph-ingress.yaml
|
||||
attachments-ingress.yaml # attachments.notes.aleshym.co -> notesnook-s3:9000
|
||||
argocd/notesnook.yaml
|
||||
```
|
||||
|
||||
`.gitignore` already covers `apps/**/*-secret.yaml`.
|
||||
|
||||
## Data migration procedure
|
||||
|
||||
Runs during the cutover window; both stacks reachable from the migration host.
|
||||
|
||||
1. **Mongo:** `mongodump` the `identity` and `notesnook` DBs from the compose
|
||||
Mongo (`docker exec notesnook-db mongodump --archive`), stream to the k8s Mongo
|
||||
via `kubectl exec` + `mongorestore --archive --nsInclude 'identity.*'
|
||||
--nsInclude 'notesnook.*'`. Verify collection counts match on both sides.
|
||||
2. **MinIO:** `mc mirror` the `attachments` bucket from the compose MinIO to the
|
||||
k8s MinIO (mirror via a temporary `mc` pod/job with both aliases, or
|
||||
port-forward). Verify object count/size.
|
||||
3. **Sanity:** confirm identity DB user document and notesnook sync docs present.
|
||||
|
||||
## Cutover procedure
|
||||
|
||||
1. Deploy the empty k8s stack (commit → ArgoCD sync). Verify all pods healthy,
|
||||
ingress created, **TLS certs issued** for all 5 hosts (temporary test hostnames
|
||||
or `/etc/hosts` override so we can validate before flipping real DNS).
|
||||
2. Announce brief downtime. Stop client sync / freeze writes on old stack
|
||||
(`docker compose stop` the app services, keep DB+MinIO up for the dump).
|
||||
3. Run the data migration (above).
|
||||
4. **DNS cutover on `10.10.0.8`:** repoint
|
||||
`auth`/`sync`/`events`/`monograph`/`attachments`.notes.aleshym.co **and** apex
|
||||
`notes.aleshym.co` from `10.10.0.5` → `10.10.0.100`. Lower TTL beforehand if
|
||||
possible; flush caches after.
|
||||
5. Verify end-to-end against k8s: request a login code (email arrives), log in,
|
||||
sync a note, open an attachment, load a monograph link.
|
||||
6. `docker compose down` the old stack (data retained) — rollback = flip DNS back
|
||||
and `docker compose up -d`.
|
||||
|
||||
## Rollback
|
||||
|
||||
Until decommission, rollback is: repoint the 6 records on `10.10.0.8` back to
|
||||
`10.10.0.5` and `docker compose up -d` on the old host. Because writes were frozen
|
||||
during cutover, no data diverges.
|
||||
|
||||
## Decommission (≈1 week after stable cutover)
|
||||
|
||||
Remove the compose stack and its data on `10.10.0.5`
|
||||
(`docker compose down -v`, remove `~/notesnook`), and reclaim the LXC if no longer
|
||||
needed. Update memory notes to point at the k8s deployment.
|
||||
|
||||
## Verification / success criteria
|
||||
|
||||
- All Notesnook pods `Running`/healthy; Mongo RS `PRIMARY`; MinIO bucket present.
|
||||
- All 5 ingress hosts serve valid Let's Encrypt certs.
|
||||
- Existing account logs in (email code delivered) — **no re-signup**.
|
||||
- Pre-existing notes + attachments visible in the client after sync.
|
||||
- Monograph public link resolves.
|
||||
|
||||
## Open items / access still needed
|
||||
|
||||
- **Git push access** to `git.aleshym.co/funman300/k3s-homelab` (clone was
|
||||
anonymous/read-only). Decide branch vs PR workflow.
|
||||
- **Edit access to internal DNS `10.10.0.8`** for the cutover (mechanism/creds TBD
|
||||
— AdGuard/Pi-hole/unbound?).
|
||||
- **`KNOWN_PROXIES`:** old value empty; behind Traefik the sync/identity servers
|
||||
may need the Traefik pod source CIDR here for correct client-IP/proxy handling.
|
||||
Confirm during implementation.
|
||||
- **Public access path:** are the `notes.*` hosts reachable externally (Cloudflare
|
||||
tunnel / netbird), or LAN/VPN-only? Affects whether any Cloudflare records also
|
||||
need updating, or just internal DNS.
|
||||
- **Twilio SMS 2FA:** present in old `.env`; assumed unused (email 2FA works). Omit
|
||||
unless confirmed in use.
|
||||
```
|
||||
Reference in New Issue
Block a user