├── .git-crypt ├── .gitattributes └── keys │ └── default │ └── 0 │ └── 74DA356B47F5F8DB40774E6CEB4B534ED4D71463.gpg ├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── ansible.cfg ├── dev ├── k3s │ └── lab-cluster │ │ ├── lab-cluster.auto.tfvars │ │ ├── lab-cluster.tf │ │ └── provider.tf └── proxmox │ ├── talos-bgp │ ├── main.tf │ ├── nodes.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf │ ├── talos-pseudoprod │ ├── main.tf │ ├── nodes.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf │ └── ubuntu-template.sh ├── files ├── fancontrol │ └── c137-fancontrol ├── sanoid │ └── c137.conf └── scripts │ └── git-init.sh ├── group_vars ├── all.yaml ├── c137.yaml ├── caddy.yaml ├── ktz-cloud.yaml ├── ktz-core.yaml ├── proxmorph.yaml └── secrets.yaml ├── hosts.ini ├── justfile ├── renovate.json ├── roles ├── local-neo-yeet │ └── tasks │ │ ├── file-sharing.yaml │ │ ├── main.yaml │ │ └── users.yaml └── nix-manager │ ├── defaults │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ ├── flake.nix.j2 │ └── home.nix.j2 ├── run.yaml └── services ├── c137 ├── 01-traefik │ └── compose.yaml ├── 02-mediaservers │ └── compose.yaml ├── 03-arrmatey │ └── compose.yaml └── 04-monitoring │ └── compose.yaml ├── ktz-cloud ├── 01-traefik │ └── compose.yaml ├── 02-monitoring │ └── compose.yaml ├── 03-static-sites │ └── compose.yaml ├── 04-blogs │ └── compose.yaml ├── 05-unifi │ └── compose.yaml ├── 06-apps │ └── compose.yaml └── 07-search │ └── compose.yaml ├── ktz-core ├── 01-ingress │ └── compose.yaml └── 02-apps │ └── compose.yaml ├── lxc-apps ├── 01-traefik │ └── compose.yaml ├── 02-apps │ └── compose.yaml └── 99-testing │ └── compose.yaml ├── lxc-id ├── compose.yaml └── karakeep.yaml ├── lxc-immich-app ├── compose.yaml └── immich-app-lxc.conf ├── lxc-immich-ml └── compose.yaml ├── lxc-ollama └── compose.yaml └── maple-apps ├── 02-apps └── compose.yaml └── 03-media └── compose.yaml /.git-crypt/.gitattributes: -------------------------------------------------------------------------------- 1 | # Do not edit this file. To specify the files to encrypt, create your own 2 | # .gitattributes file in the directory where your files are. 3 | * !filter !diff 4 | *.gpg binary 5 | -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/74DA356B47F5F8DB40774E6CEB4B534ED4D71463.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ironicbadger/infra/14dae115f06ae5bbb9380364cbdfad649dbd5e79/.git-crypt/keys/default/0/74DA356B47F5F8DB40774E6CEB4B534ED4D71463.gpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .gitattributes !filter !diff 2 | **/*.enc filter=git-crypt diff=git-crypt 3 | **/*.enc.* filter=git-crypt diff=git-crypt 4 | **/*.enc/** filter=git-crypt diff=git-crypt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/macos,linux,vagrant,windows,visualstudiocode 2 | 3 | ### Linux ### 4 | *~ 5 | 6 | # temporary files which can be created if a process still has a handle open of a deleted file 7 | .fuse_hidden* 8 | 9 | # KDE directory preferences 10 | .directory 11 | 12 | # Linux trash folder which might appear on any partition or disk 13 | .Trash-* 14 | 15 | # .nfs files are created when an open file is removed but is still being accessed 16 | .nfs* 17 | 18 | ### macOS ### 19 | *.DS_Store 20 | .AppleDouble 21 | .LSOverride 22 | 23 | # Icon must end with two \r 24 | Icon 25 | 26 | # Thumbnails 27 | ._* 28 | 29 | # Files that might appear in the root of a volume 30 | .DocumentRevisions-V100 31 | .fseventsd 32 | .Spotlight-V100 33 | .TemporaryItems 34 | .Trashes 35 | .VolumeIcon.icns 36 | .com.apple.timemachine.donotpresent 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | ### Vagrant ### 46 | .vagrant/ 47 | *.box 48 | 49 | ### VisualStudioCode ### 50 | #.vscode/* 51 | #!.vscode/settings.json 52 | !.vscode/tasks.json 53 | !.vscode/launch.json 54 | !.vscode/extensions.json 55 | .history 56 | 57 | ### Windows ### 58 | # Windows thumbnail cache files 59 | Thumbs.db 60 | ehthumbs.db 61 | ehthumbs_vista.db 62 | 63 | # Folder config file 64 | Desktop.ini 65 | 66 | # Recycle Bin used on file shares 67 | $RECYCLE.BIN/ 68 | 69 | # Windows Installer files 70 | *.cab 71 | *.msi 72 | *.msm 73 | *.msp 74 | 75 | # Windows shortcuts 76 | *.lnk 77 | 78 | 79 | # End of https://www.gitignore.io/api/macos,linux,vagrant,windows,visualstudiocode 80 | #.vscode/settings.json 81 | 82 | 83 | # Created by https://www.gitignore.io/api/terraform 84 | 85 | ### Terraform ### 86 | # Local .terraform directories 87 | **/.terraform/* 88 | 89 | # .tfstate files 90 | *.tfstate 91 | *.tfstate.* 92 | *.hcl 93 | *.tfvars 94 | 95 | # python 96 | __pycache__ 97 | 98 | # Crash log files 99 | crash.log 100 | 101 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 102 | # .tfvars files are managed as part of configuration and so should be included in 103 | # version control. 104 | # 105 | # example.tfvars 106 | 107 | # End of https://www.gitignore.io/api/terraform 108 | cloud/production/hosts 109 | ktz-rh-vpn 110 | galaxy_roles 111 | .vault-password 112 | vault.yaml 113 | 114 | .editorconfig 115 | 116 | credentials.auto.tfvars -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "roles/ansible-role-caddy"] 2 | path = roles/ansible-role-caddy 3 | url = https://github.com/ironicbadger/ansible-role-caddy.git 4 | [submodule "roles/ironicbadger.caddy"] 5 | path = roles/ironicbadger.caddy 6 | url = https://github.com/ironicbadger/ansible-role-caddy.git 7 | [submodule "roles/ironicbadger.docker-compose-generator"] 8 | path = roles/ironicbadger.docker-compose-generator 9 | url = git@github.com:ironicbadger/ansible-role-docker-compose-generator.git 10 | [submodule "roles/ironicbadger.figurine"] 11 | path = roles/ironicbadger.figurine 12 | url = git@github.com:ironicbadger/ansible-role-figurine.git 13 | [submodule "roles/ironicbadger.tailscale-routes-fix"] 14 | path = roles/ironicbadger.tailscale-routes-fix 15 | url = git@github.com:ironicbadger/tailscale-routes-fix.git 16 | [submodule "roles/ironicbadger.lxc-debian-nvidia"] 17 | path = roles/ironicbadger.lxc-debian-nvidia 18 | url = git@ssh.github.com:ironicbadger/lxc-debian-nvidia.git 19 | [submodule "roles/ironicbadger.packages"] 20 | path = roles/ironicbadger.packages 21 | url = git@ssh.github.com:ironicbadger/ansible-role-packages.git 22 | [submodule "roles/ironicbadger.mounts"] 23 | path = roles/ironicbadger.mounts 24 | url = git@github.com:ironicbadger/ansible-role-drivemounts.git 25 | [submodule "roles/ironicbadger.proxmox-host-nvidia-setup"] 26 | path = roles/ironicbadger.proxmox-host-nvidia-setup 27 | url = git@ssh.github.com:ironicbadger/proxmox-host-nvidia-setup.git 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ironicbadger/infra 2 | 3 | This branch is being completely rewritten for the start of 2025. Do not use. -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | retry_files_enabled = False 4 | roles_path = $PWD/galaxy_roles:$PWD/roles:$PWD/submodules 5 | inventory = ./hosts.ini 6 | vault_password_file = ./.vault-password 7 | remote_user = alex 8 | callback_whitelist = profile_tasks, timer 9 | 10 | [privilege_escalation] 11 | become_ask_pass = False 12 | 13 | [git] 14 | recursive = true # git submodules -------------------------------------------------------------------------------- /dev/k3s/lab-cluster/lab-cluster.auto.tfvars: -------------------------------------------------------------------------------- 1 | proxmox_api_url = "https://10.42.37.10:8006/api2/json" 2 | proxmox_api_token_id = "root@pam!root" 3 | proxmox_api_token_secret = "bcc0518c-a67c-4efa-8c1e-d6e44653aad4" 4 | 5 | lab_cluster_name = "k3s-lab" 6 | 7 | lab_cluster_nodes = { 8 | "master1" = { 9 | "mac" = "32:ee:a4:94:9d:ad" 10 | "target" = "clarkson" 11 | "disk" = "15G" 12 | "storage" = "shared" 13 | } 14 | "master2" = { 15 | "mac" = "b6:16:fe:7d:19:af" 16 | "target" = "hammond" 17 | "disk" = "15G" 18 | "storage" = "shared" 19 | } 20 | "master3" = { 21 | "mac" = "b6:59:d3:fa:aa:c0" 22 | "target" = "may" 23 | "disk" = "15G" 24 | "storage" = "shared" 25 | } 26 | "node1" = { 27 | "mac" = "5a:b9:00:2e:bb:c2" 28 | "target" = "clarkson" 29 | "disk" = "25G" 30 | "storage" = "shared" 31 | } 32 | "node2" = { 33 | "mac" = "46:04:13:26:34:bd" 34 | "target" = "hammond" 35 | "disk" = "25G" 36 | "storage" = "shared" 37 | } 38 | "node3" = { 39 | "mac" = "46:04:13:26:34:4f" 40 | "target" = "may" 41 | "disk" = "25G" 42 | "storage" = "shared" 43 | } 44 | } -------------------------------------------------------------------------------- /dev/k3s/lab-cluster/lab-cluster.tf: -------------------------------------------------------------------------------- 1 | resource "proxmox_vm_qemu" "node" { 2 | for_each = var.lab_cluster_nodes 3 | 4 | name = "${var.lab_cluster_name}-${each.key}" 5 | target_node = each.value["target"] 6 | vmid = 2000 + index(keys(var.lab_cluster_nodes), each.key) 7 | 8 | clone = "ubuntu-2404" 9 | full_clone = true 10 | cores = 4 11 | memory = 4096 12 | agent = 0 13 | vga = "std" 14 | 15 | network { 16 | id = 0 17 | bridge = "vmbr0" 18 | model = "virtio" 19 | macaddr = each.value["mac"] 20 | } 21 | disk { 22 | type = "disk" 23 | slot = "scsi0" 24 | size = each.value["disk"] 25 | storage = each.value["storage"] 26 | } 27 | 28 | lifecycle { 29 | ignore_changes = [ 30 | ipconfig0, #dhcp related after initial clone 31 | ciuser, 32 | sshkeys 33 | ] 34 | } 35 | } -------------------------------------------------------------------------------- /dev/k3s/lab-cluster/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | 3 | required_providers { 4 | proxmox = { 5 | source = "telmate/proxmox" 6 | version = "3.0.1-rc6" 7 | } 8 | } 9 | } 10 | 11 | variable "proxmox_api_url" { 12 | type = string 13 | } 14 | 15 | variable "proxmox_api_token_id" { 16 | type = string 17 | sensitive = true 18 | } 19 | 20 | variable "proxmox_api_token_secret" { 21 | type = string 22 | sensitive = true 23 | } 24 | 25 | variable "lab_cluster_name" { 26 | type = string 27 | } 28 | 29 | variable "lab_cluster_nodes" { 30 | type = map 31 | } 32 | 33 | provider "proxmox" { 34 | pm_api_url = var.proxmox_api_url 35 | pm_api_token_id = var.proxmox_api_token_id 36 | pm_api_token_secret = var.proxmox_api_token_secret 37 | 38 | pm_tls_insecure = true 39 | #pm_parallel = 10 40 | 41 | # pm_log_enable = false 42 | # pm_log_file = "terraform-plugin-proxmox.log" 43 | # pm_debug = true 44 | # pm_log_levels = { 45 | # _default = "debug" 46 | # _capturelog = "" 47 | # } 48 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-bgp/main.tf: -------------------------------------------------------------------------------- 1 | # Control plane nodes 2 | resource "proxmox_virtual_machine" "talos_control_plane" { 3 | for_each = local.control_plane_nodes 4 | 5 | name = each.key 6 | node = "c137" 7 | vm_id = each.value.id 8 | clone = "template-talos-qemu" 9 | clone_wait = 5 10 | full_clone = true 11 | 12 | # VM specifications 13 | memory { 14 | dedicated = each.value.memory 15 | } 16 | cpu { 17 | cores = each.value.cores 18 | sockets = 1 19 | type = "host" 20 | } 21 | agent { 22 | enabled = true # Enable QEMU agent for Talos 23 | } 24 | 25 | # Set boot order 26 | boot_order = ["virtio0", "ide2", "net0"] 27 | 28 | network_device { 29 | bridge = "vmbr0" 30 | model = "virtio" 31 | mac_address = each.value.mac 32 | } 33 | 34 | disk { 35 | datastore_id = "vmstore-nfs" 36 | file_format = "raw" 37 | interface = "virtio0" 38 | size = each.value.disk_size 39 | } 40 | 41 | # Add ISO as a separate disk 42 | cdrom { 43 | enabled = true 44 | iso_file = "c137-isos:iso/talos-1.9.5-nocloud-amd64-qemuguest-tailscale.iso" 45 | } 46 | 47 | # Timeouts 48 | lifecycle { 49 | create_before_destroy = true 50 | ignore_changes = [ 51 | boot_order 52 | ] 53 | } 54 | 55 | timeouts { 56 | create = "5m" 57 | update = "5m" 58 | delete = "3m" 59 | } 60 | } 61 | 62 | # Worker nodes 63 | resource "proxmox_virtual_machine" "talos_workers" { 64 | for_each = local.worker_nodes 65 | 66 | name = each.key 67 | node = "c137" 68 | vm_id = each.value.id 69 | clone = "template-talos-qemu" 70 | clone_wait = 5 71 | full_clone = true 72 | 73 | # VM specifications 74 | memory { 75 | dedicated = each.value.memory 76 | } 77 | cpu { 78 | cores = each.value.cores 79 | sockets = 1 80 | type = "host" 81 | } 82 | agent { 83 | enabled = true # Enable QEMU agent for Talos workers as well 84 | } 85 | 86 | # Set boot order 87 | boot_order = ["virtio0", "ide2", "net0"] 88 | 89 | network_device { 90 | bridge = "vmbr0" 91 | model = "virtio" 92 | mac_address = each.value.mac 93 | } 94 | 95 | disk { 96 | datastore_id = "vmstore-nfs" 97 | file_format = "raw" 98 | interface = "virtio0" 99 | size = each.value.disk_size 100 | } 101 | 102 | # Add ISO as a separate disk 103 | cdrom { 104 | enabled = true 105 | iso_file = "c137-isos:iso/talos-1.9.5-nocloud-amd64-qemuguest-tailscale.iso" 106 | } 107 | 108 | # Timeouts 109 | lifecycle { 110 | create_before_destroy = true 111 | ignore_changes = [ 112 | boot_order 113 | ] 114 | } 115 | 116 | timeouts { 117 | create = "5m" 118 | update = "5m" 119 | delete = "3m" 120 | } 121 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-bgp/nodes.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # Base node configurations 3 | control_plane_base = { 4 | memory = 4096 5 | cores = 4 6 | disk_size = "30G" # Added unit G 7 | } 8 | 9 | worker_base = { 10 | memory = 8192 11 | cores = 8 12 | disk_size = "250G" # Added unit G 13 | } 14 | 15 | # Node definitions 16 | nodes = { 17 | # Control plane nodes 18 | "talos-ctl1" = merge(local.control_plane_base, { 19 | id = 5001 20 | mac = "bc:24:11:b6:bd:66" 21 | role = "control_plane" 22 | }) 23 | "talos-ctl2" = merge(local.control_plane_base, { 24 | id = 5002 25 | mac = "bc:24:11:3a:b7:75" 26 | role = "control_plane" 27 | }) 28 | "talos-ctl3" = merge(local.control_plane_base, { 29 | id = 5003 30 | mac = "bc:24:11:f7:79:4a" 31 | role = "control_plane" 32 | }) 33 | 34 | # Worker nodes 35 | "talos-wrk1" = merge(local.worker_base, { 36 | id = 5101 37 | mac = "bc:24:11:35:73:ad" 38 | role = "worker" 39 | }) 40 | "talos-wrk2" = merge(local.worker_base, { 41 | id = 5102 42 | mac = "bc:24:11:2a:c4:6f" 43 | role = "worker" 44 | }) 45 | "talos-wrk3" = merge(local.worker_base, { 46 | id = 5103 47 | mac = "bc:24:11:ce:86:16" 48 | role = "worker" 49 | }) 50 | } 51 | 52 | # Filter nodes by role 53 | control_plane_nodes = { 54 | for name, node in local.nodes : name => node if node.role == "control_plane" 55 | } 56 | 57 | worker_nodes = { 58 | for name, node in local.nodes : name => node if node.role == "worker" 59 | } 60 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-bgp/outputs.tf: -------------------------------------------------------------------------------- 1 | output "control_plane_ips" { 2 | description = "IP addresses of the control plane nodes (if DHCP assigns them)" 3 | value = { 4 | for name, vm in proxmox_virtual_machine.talos_control_plane : name => vm.ipv4_addresses 5 | } 6 | } 7 | 8 | output "worker_node_ips" { 9 | description = "IP addresses of the worker nodes (if DHCP assigns them)" 10 | value = { 11 | for name, vm in proxmox_virtual_machine.talos_workers : name => vm.ipv4_addresses 12 | } 13 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-bgp/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = "0.38.1" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.proxmox_api_url 12 | api_token = "${var.proxmox_user}=${var.proxmox_password}" 13 | insecure = true 14 | ssh { 15 | agent = true 16 | } 17 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-bgp/variables.tf: -------------------------------------------------------------------------------- 1 | variable "proxmox_api_url" { 2 | description = "The Proxmox API URL" 3 | type = string 4 | sensitive = true 5 | } 6 | 7 | variable "proxmox_user" { 8 | description = "Proxmox API token ID (e.g., 'root@pam!terraform')" 9 | type = string 10 | sensitive = true 11 | } 12 | 13 | variable "proxmox_password" { 14 | description = "Proxmox API token secret" 15 | type = string 16 | sensitive = true 17 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-pseudoprod/main.tf: -------------------------------------------------------------------------------- 1 | # Control plane nodes 2 | resource "proxmox_vm_qemu" "talos_control_plane" { 3 | for_each = local.control_plane_nodes 4 | 5 | name = each.key 6 | target_node = "c137" 7 | vmid = each.value.id 8 | clone = "template-talos-qemu" 9 | clone_wait = 5 10 | full_clone = true 11 | 12 | # VM specifications 13 | memory = each.value.memory 14 | cores = each.value.cores 15 | sockets = 1 16 | cpu_type = "host" 17 | agent = 1 18 | 19 | # Set boot order to include cdrom 20 | boot = "order=virtio0;ide2;net0" 21 | 22 | network { 23 | id = 0 24 | model = "virtio" 25 | bridge = "vmbr0" 26 | macaddr = each.value.mac 27 | } 28 | 29 | disk { 30 | slot = "virtio0" 31 | type = "disk" 32 | storage = "vmstore-nfs" 33 | size = each.value.disk_size 34 | format = "raw" 35 | } 36 | 37 | disk { 38 | slot = "ide2" 39 | type = "cdrom" 40 | iso = "c137-isos:iso/talos-1.9.5-nocloud-amd64-qemuguest-tailscale.iso" 41 | } 42 | } 43 | 44 | # Worker nodes 45 | resource "proxmox_vm_qemu" "talos_workers" { 46 | for_each = local.worker_nodes 47 | 48 | name = each.key 49 | target_node = "c137" 50 | vmid = each.value.id 51 | clone = "template-talos-qemu" 52 | clone_wait = 5 53 | full_clone = true 54 | 55 | memory = each.value.memory 56 | cores = each.value.cores 57 | sockets = 1 58 | cpu_type = "host" 59 | agent = 1 60 | 61 | # Set boot order to include cdrom 62 | boot = "order=virtio0;ide2;net0" 63 | 64 | network { 65 | id = 0 66 | model = "virtio" 67 | bridge = "vmbr0" 68 | macaddr = each.value.mac 69 | } 70 | 71 | disk { 72 | slot = "virtio0" 73 | type = "disk" 74 | storage = "vmstore-nfs" 75 | size = each.value.disk_size 76 | format = "raw" 77 | } 78 | 79 | disk { 80 | slot = "ide2" 81 | type = "cdrom" 82 | iso = "c137-isos:iso/talos-1.9.5-nocloud-amd64-qemuguest-tailscale.iso" 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-pseudoprod/nodes.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # Base node configurations 3 | control_plane_base = { 4 | memory = 4096 5 | cores = 4 6 | disk_size = "30G" 7 | } 8 | 9 | worker_base = { 10 | memory = 8192 11 | cores = 8 12 | disk_size = "250G" 13 | } 14 | 15 | # Node definitions 16 | nodes = { 17 | # Control plane nodes 18 | "talos-ctl1" = merge(local.control_plane_base, { 19 | id = 5001 20 | mac = "bc:24:11:b6:bd:66" 21 | role = "control_plane" 22 | }) 23 | "talos-ctl2" = merge(local.control_plane_base, { 24 | id = 5002 25 | mac = "bc:24:11:3a:b7:75" 26 | role = "control_plane" 27 | }) 28 | "talos-ctl3" = merge(local.control_plane_base, { 29 | id = 5003 30 | mac = "bc:24:11:f7:79:4a" 31 | role = "control_plane" 32 | }) 33 | 34 | # Worker nodes 35 | "talos-wrk1" = merge(local.worker_base, { 36 | id = 5101 37 | mac = "bc:24:11:35:73:ad" 38 | role = "worker" 39 | }) 40 | "talos-wrk2" = merge(local.worker_base, { 41 | id = 5102 42 | mac = "bc:24:11:2a:c4:6f" 43 | role = "worker" 44 | }) 45 | "talos-wrk3" = merge(local.worker_base, { 46 | id = 5103 47 | mac = "bc:24:11:ce:86:16" 48 | role = "worker" 49 | }) 50 | } 51 | 52 | # Filter nodes by role 53 | control_plane_nodes = { 54 | for name, node in local.nodes : name => node if node.role == "control_plane" 55 | } 56 | 57 | worker_nodes = { 58 | for name, node in local.nodes : name => node if node.role == "worker" 59 | } 60 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-pseudoprod/outputs.tf: -------------------------------------------------------------------------------- 1 | output "control_plane_ips" { 2 | description = "IP addresses of the control plane nodes (if DHCP assigns them)" 3 | value = { 4 | for name, vm in proxmox_vm_qemu.talos_control_plane : name => vm.default_ipv4_address 5 | } 6 | } 7 | 8 | output "worker_node_ips" { 9 | description = "IP addresses of the worker nodes (if DHCP assigns them)" 10 | value = { 11 | for name, vm in proxmox_vm_qemu.talos_workers : name => vm.default_ipv4_address 12 | } 13 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-pseudoprod/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "telmate/proxmox" 5 | version = "3.0.1-rc8" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | pm_api_url = var.proxmox_api_url 12 | pm_api_token_id = var.proxmox_user 13 | pm_api_token_secret = var.proxmox_password 14 | pm_tls_insecure = true 15 | } -------------------------------------------------------------------------------- /dev/proxmox/talos-pseudoprod/variables.tf: -------------------------------------------------------------------------------- 1 | variable "proxmox_api_url" { 2 | description = "The Proxmox API URL" 3 | type = string 4 | sensitive = true 5 | } 6 | 7 | variable "proxmox_user" { 8 | description = "Proxmox API token ID (e.g., 'root@pam!terraform')" 9 | type = string 10 | sensitive = true 11 | } 12 | 13 | variable "proxmox_password" { 14 | description = "Proxmox API token secret" 15 | type = string 16 | sensitive = true 17 | } -------------------------------------------------------------------------------- /dev/proxmox/ubuntu-template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # credit: https://www.apalrd.net/posts/2023/pve_cloud/ 3 | 4 | function create_template() { 5 | echo "Creating template $2 ($1)" 6 | 7 | qm create $1 --name $2 --ostype l26 8 | qm set $1 --net0 virtio,bridge=vmbr0 9 | qm set $1 --serial0 socket --vga serial0 10 | qm set $1 --memory 1024 --cores 4 --cpu host 11 | qm set $1 --scsi0 ${storage}:0,import-from="$(pwd)/$3",discard=on 12 | qm set $1 --boot order=scsi0 --scsihw virtio-scsi-single 13 | qm set $1 --agent enabled=1,fstrim_cloned_disks=1 14 | qm set $1 --ide2 ${storage}:cloudinit 15 | qm set $1 --ipconfig0 "ip6=auto,ip=dhcp" 16 | qm set $1 --sshkeys ${ssh_keyfile} 17 | qm set $1 --ciuser ${username} 18 | qm disk resize $1 scsi0 8G 19 | qm template $1 20 | 21 | #Remove file when done 22 | rm $3 23 | } 24 | 25 | export ssh_keyfile=/root/.ssh/id_ed25519.pub 26 | export username=zaphod 27 | export storage=shared 28 | 29 | ## Ubuntu 30 | #24.04 (Noble Numbat) LTS 31 | wget "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img" 32 | virt-customize -a ubuntu-24.04-server-cloudimg-amd64.img --install qemu-guest-agent 33 | create_template 9000 "ubuntu-2404" "ubuntu-24.04-server-cloudimg-amd64.img" -------------------------------------------------------------------------------- /files/fancontrol/c137-fancontrol: -------------------------------------------------------------------------------- 1 | # cat /etc/fancontrol 2 | # Configuration file generated by pwmconfig, changes will be lost 3 | INTERVAL=10 4 | DEVPATH=hwmon23=devices/platform/coretemp.0 hwmon19=devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13:1.0/0003:1B1C:0C10.0001 5 | DEVNAME=hwmon23=coretemp hwmon19=corsaircpro 6 | FCTEMPS=hwmon19/pwm5=hwmon23/temp1_input hwmon19/pwm4=hwmon23/temp1_input hwmon19/pwm3=hwmon23/temp1_input hwmon19/pwm2=hwmon23/temp1_input hwmon19/pwm1=hwmon23/temp1_input 7 | FCFANS=hwmon19/pwm5=hwmon19/fan5_input hwmon19/pwm4=hwmon19/fan4_input hwmon19/pwm3=hwmon19/fan3_input hwmon19/pwm2=hwmon19/fan2_input hwmon19/pwm1=hwmon19/fan1_input 8 | MINTEMP=hwmon19/pwm5=50 hwmon19/pwm4=50 hwmon19/pwm3=50 hwmon19/pwm2=50 hwmon19/pwm1=50 9 | MAXTEMP=hwmon19/pwm5=70 hwmon19/pwm4=70 hwmon19/pwm3=70 hwmon19/pwm2=70 hwmon19/pwm1=70 10 | MINSTART=hwmon19/pwm5=145 hwmon19/pwm4=145 hwmon19/pwm3=195 hwmon19/pwm2=145 hwmon19/pwm1=145 11 | MINSTOP=hwmon19/pwm5=145 hwmon19/pwm4=145 hwmon19/pwm3=195 hwmon19/pwm2=145 hwmon19/pwm1=145 12 | MINPWM=hwmon19/pwm5=145 hwmon19/pwm4=145 hwmon19/pwm3=195 hwmon19/pwm2=145 hwmon19/pwm1=145 13 | MAXPWM=hwmon19/pwm5=235 hwmon19/pwm4=235 hwmon19/pwm3=235 hwmon19/pwm2=235 hwmon19/pwm1=235 -------------------------------------------------------------------------------- /files/sanoid/c137.conf: -------------------------------------------------------------------------------- 1 | [bigrust20/data] 2 | daily = 3 3 | monthly = 3 4 | autosnap = yes 5 | autoprune = yes 6 | recursive = yes 7 | 8 | [bigrust20/photos] 9 | daily = 7 10 | monthly = 6 11 | yearly = 1 12 | autosnap = yes 13 | autoprune = yes 14 | recursive = yes 15 | 16 | [bigrust20/video] 17 | daily = 7 18 | monthly = 3 19 | autosnap = yes 20 | autoprune = yes 21 | recursive = yes -------------------------------------------------------------------------------- /files/scripts/git-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # sets up a pre-commit hook to ensure that vault.yaml is encrypted 3 | # 4 | # credit goes to nick busey from homelabos for this neat little trick 5 | # https://gitlab.com/NickBusey/HomelabOS/-/issues/355 6 | 7 | if [ -d .git/ ]; then 8 | rm .git/hooks/pre-commit 9 | cat <> .git/hooks/pre-commit 10 | if ( git show :group_vars/secrets.yaml | grep -q "\$ANSIBLE_VAULT;" ); then 11 | echo "Vault Encrypted. Safe to commit." 12 | else 13 | echo "Vault not encrypted! Run 'make encrypt' and try again." 14 | exit 1 15 | fi 16 | EOT 17 | 18 | fi 19 | 20 | chmod +x .git/hooks/pre-commit -------------------------------------------------------------------------------- /group_vars/all.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ironicbadger.tailscale-routes-fix 4 | tailscale_routes_fix_lan_subnet: 10.42.0.0/21 5 | tailscale_routes_table: main 6 | 7 | # ironicbadger.docker-compose-generator 8 | host_timezone: "America/New_York" 9 | wd_domain_me: "wd.{{ domain_me }}" 10 | maple_domain_me: "maple.{{ domain_me }}" 11 | apps_domain_me: "apps.{{ domain_me }}" -------------------------------------------------------------------------------- /group_vars/c137.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # ironicbadger.docker-compose-generator 3 | host_timezone: "US/Eastern" 4 | docker_compose_hostname: "c137" 5 | 6 | appdata_path: /mnt/nvmeu2/appdata 7 | download_path: /mnt/downloads 8 | storage_path: /mnt/jbod 9 | bigrust_path: /mnt/bigrust20 10 | wd_domain_me: "wd.{{ domain_me }}" 11 | m_wd_domain_me: "m.{{ wd_domain_me }}" 12 | 13 | docker_compose_generator_uid: "1001" 14 | docker_compose_generator_gid: "1001" 15 | 16 | disabled_compose_files: [] 17 | 18 | # ironicbadger.packages 19 | packages_perform_cleanup: True 20 | package_list: 21 | - bcache-tools 22 | - btrfs-progs 23 | - curl 24 | - git 25 | - htop 26 | - intel-gpu-tools 27 | - proxmox-kernel-6.14 28 | - samba 29 | - samba-common-bin 30 | - smbclient 31 | - sudo 32 | - tmux 33 | - wget 34 | - which 35 | 36 | # ironicbadger.nix-home-manager 37 | nix_packages: 38 | # Unstable packages 39 | - nixpkgs-unstable.beszel 40 | 41 | # Stable packages 42 | - btop 43 | - coreutils 44 | - cifs-utils 45 | - drill 46 | - du-dust 47 | - dua 48 | - duf 49 | - entr 50 | - fastfetch 51 | - fd 52 | - fdupes 53 | - ffmpeg 54 | - figurine 55 | - go 56 | - iperf3 57 | - ipmitool 58 | - just 59 | - mc 60 | - mergerfs 61 | - ncdu 62 | - neofetch 63 | - nfs-utils 64 | - nmap 65 | - ripgrep 66 | - smartmontools 67 | - tree 68 | - unzip 69 | - watch 70 | - wget 71 | - wireguard-tools 72 | - zoxide 73 | 74 | # nix-manager configuration 75 | nix_user: "root" 76 | nix_group: "root" 77 | nix_home: "/root" 78 | install_home_manager: true 79 | git_username: "Alex Kretzschmar" 80 | git_email: "{{ vault_cloudflare_account_email }}" 81 | 82 | # ironicbadger.mounts 83 | automount_drives: true 84 | drive_mounts: 85 | # mergings 86 | - mountpoint: "/mnt/jbod" 87 | device: "/mnt/disks/disk*" 88 | fstype: "mergerfs" 89 | options: 90 | - "defaults" 91 | - "minfreespace=250G" 92 | - "allow_other" 93 | - "use_ino" 94 | - "cache.files=partial" 95 | - "dropcacheonclose=true" 96 | - "fsname=mergerfs-jbod" 97 | # data disks 98 | - mountpoint: "/mnt/disks/disk1" 99 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_3ZGA70DZ-part1" 100 | fstype: "btrfs" 101 | options: 102 | - "subvol=data" 103 | - "compress=zstd" 104 | - "defaults" 105 | - mountpoint: "/mnt/disks/disk2" 106 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_2TGGDS5Z-part1" 107 | fstype: "btrfs" 108 | options: 109 | - "subvol=data" 110 | - "compress=zstd" 111 | - "defaults" 112 | - mountpoint: "/mnt/disks/disk3" 113 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_2GH0M6HS-part1" 114 | fstype: "btrfs" 115 | options: 116 | - "subvol=data" 117 | - "compress=zstd" 118 | - "defaults" -------------------------------------------------------------------------------- /group_vars/caddy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # ironicbadger.figurine 3 | figurine_name: us-wd-caddy 4 | 5 | # ironicbadger.caddy 6 | caddy_tls_providers: 7 | - provider: cloudflare 8 | challenge_type: dns 9 | provider_api_token: "{{ cloudflare_token_wd_caddy }}" 10 | resolver_ip: 1.1.1.1 11 | caddy_endpoints: 12 | ## USA - WD 13 | - friendly_name: wildcard for *.wd.ktz.me 14 | fqdn: '*.wd.ktz.me' 15 | tls_provider: cloudflare 16 | wildcard_endpoints: 17 | ## Network Core services 18 | - friendly_name: unifi 19 | fqdn: unifi.wd.ktz.me 20 | upstream: https://10.42.0.254:443 21 | tls_insecure: true 22 | - friendly_name: dns 23 | fqdn: dns.wd.ktz.me 24 | upstream: http://10.42.0.253 25 | ## Core Apps 26 | - friendly_name: blueiris 27 | fqdn: bi.wd.ktz.me 28 | upstream: http://10.42.2.1:83 29 | - friendly_name: beszel 30 | fqdn: beszel.wd.ktz.me 31 | upstream: http://10.42.0.1:8090 32 | ## proxmox 33 | - friendly_name: px 34 | fqdn: px.wd.ktz.me 35 | upstream: https://10.42.1.137:8006 36 | tls_insecure: true 37 | ## Apps and Services 38 | - friendly_name: immich 39 | fqdn: immich.wd.ktz.me 40 | upstream: http://10.42.0.111:2283 41 | - friendly_name: openwebui 42 | fqdn: oui.wd.ktz.me 43 | upstream: http://10.42.0.113:8080 44 | - friendly_name: wildcard for *.kvm.wd.ktz.me 45 | fqdn: '*.kvm.wd.ktz.me' 46 | tls_provider: cloudflare 47 | wildcard_endpoints: 48 | ## PiKVMs 49 | - friendly_name: basementpikvm 50 | fqdn: basement.kvm.wd.ktz.me 51 | upstream: https://10.42.0.100:443 52 | tls_insecure: true 53 | - friendly_name: megadeskkvm 54 | fqdn: megadesk.kvm.wd.ktz.me 55 | upstream: https://10.42.0.101:443 56 | tls_insecure: true 57 | ## CA - ONT 58 | - friendly_name: wildcard for *.ont.ktz.me 59 | fqdn: '*.ont.ktz.me' 60 | tls_provider: cloudflare 61 | wildcard_endpoints: 62 | - friendly_name: moose 63 | fqdn: moose.ont.ktz.me 64 | upstream: https://100.123.207.53:8006 65 | tls_insecure: true 66 | ## UK - NR 67 | - friendly_name: wildcard for *.nr.ktz.me 68 | fqdn: '*.nr.ktz.me' 69 | tls_provider: cloudflare 70 | wildcard_endpoints: 71 | - friendly_name: snowball 72 | fqdn: snowball.nr.ktz.me 73 | upstream: https://100.88.250.125:8006 74 | tls_insecure: true 75 | ## UK - GG 76 | # - friendly_name: wildcard for *.gg.ktz.me 77 | # fqdn: '*.gg.ktz.me' 78 | # tls_provider: cloudflare 79 | # wildcard_endpoints: 80 | # - friendly_name: elrond 81 | # fqdn: dsm.gg.ktz.me 82 | # upstream: https://100.99. -------------------------------------------------------------------------------- /group_vars/ktz-cloud.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ironicbadger.docker-compose-generator 4 | host_timezone: "America/New_York" 5 | appdata_path: "/opt/appdata" -------------------------------------------------------------------------------- /group_vars/ktz-core.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # ironicbadger.caddy 3 | caddy_tls_providers: 4 | - provider: cloudflare 5 | challenge_type: dns 6 | provider_api_token: "{{ cloudflare_token_caddy }}" 7 | resolver_ip: 1.1.1.1 8 | caddy_endpoints: 9 | - friendly_name: wildcard for *.core.ktz.me 10 | fqdn: '*.core.ktz.me' 11 | tls_provider: cloudflare 12 | wildcard_endpoints: 13 | - friendly_name: dns 14 | fqdn: dns.core.ktz.me 15 | upstream: http://localhost:3000 16 | - friendly_name: smokeping 17 | fqdn: smokeping.core.ktz.me 18 | upstream: http://localhost:8007 19 | - friendly_name: glance 20 | fqdn: dash.core.ktz.me 21 | upstream: http://localhost:8006 22 | - friendly_name: wildcard for *.maple.ktz.me 23 | fqdn: '*.maple.ktz.me' 24 | tls_provider: cloudflare 25 | wildcard_endpoints: 26 | - friendly_name: px-moose 27 | fqdn: px.maple.ktz.me 28 | upstream: https://192.168.13.10:8006 29 | tls_insecure: true 30 | - friendly_name: wildcard for *.apps.ktz.me 31 | fqdn: '*.apps.ktz.me' 32 | tls_provider: cloudflare 33 | wildcard_endpoints: 34 | - friendly_name: rss 35 | fqdn: rss.apps.ktz.me 36 | upstream: http://100.88.211.94:8000 37 | - friendly_name: books 38 | fqdn: books.apps.ktz.me 39 | upstream: http://localhost:8009 -------------------------------------------------------------------------------- /group_vars/proxmorph.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # ironicbadger.docker-compose-generator 4 | host_timezone: "US/Eastern" 5 | docker_compose_hostname: "proxmorph" 6 | 7 | appdata_path: /mnt/nvme-appdata 8 | download_path: /mnt/downloads 9 | storage_path: /mnt/jbod 10 | bigrust_path: /mnt/rust 11 | wd_domain_me: "wd.{{ domain_me }}" 12 | m_wd_domain_me: "m.{{ wd_domain_me }}" 13 | 14 | docker_compose_generator_uid: "0" 15 | docker_compose_generator_gid: "0" 16 | 17 | disabled_compose_files: [] 18 | 19 | # ironicbadger.packages 20 | packages_perform_cleanup: True 21 | package_list: 22 | - bcache-tools 23 | - btrfs-progs 24 | - curl 25 | - git 26 | - htop 27 | - intel-gpu-tools 28 | - proxmox-kernel-6.14 29 | - samba 30 | - samba-common-bin 31 | - smbclient 32 | - sudo 33 | - tmux 34 | - wget 35 | - which 36 | 37 | # ironicbadger.nix-home-manager 38 | nix_packages: 39 | # Unstable packages 40 | - nixpkgs-unstable.beszel 41 | 42 | # Stable packages 43 | - btop 44 | - coreutils 45 | - cifs-utils 46 | - drill 47 | - du-dust 48 | - dua 49 | - duf 50 | - entr 51 | - fastfetch 52 | - fd 53 | - fdupes 54 | - ffmpeg 55 | - figurine 56 | - go 57 | - iperf3 58 | - ipmitool 59 | - just 60 | - mc 61 | - mergerfs 62 | - ncdu 63 | - neofetch 64 | - nfs-utils 65 | - nmap 66 | - ripgrep 67 | - smartmontools 68 | - tree 69 | - unzip 70 | - watch 71 | - wget 72 | - wireguard-tools 73 | - zoxide 74 | 75 | # ironicbadger.mounts 76 | automount_drives: true 77 | drive_mounts: 78 | # mergings 79 | - mountpoint: "/mnt/jbod" 80 | device: "/mnt/disks/disk*" 81 | fstype: "mergerfs" 82 | options: 83 | - "defaults" 84 | - "minfreespace=250G" 85 | - "allow_other" 86 | - "use_ino" 87 | - "cache.files=partial" 88 | - "dropcacheonclose=true" 89 | - "fsname=mergerfs-jbod" 90 | # data disks 91 | - mountpoint: "/mnt/disks/disk1" 92 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_3ZGA70DZ-part1" 93 | fstype: "btrfs" 94 | options: 95 | - "subvol=data" 96 | - "compress=zstd" 97 | - "defaults" 98 | - mountpoint: "/mnt/disks/disk2" 99 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_2TGGDS5Z-part1" 100 | fstype: "btrfs" 101 | options: 102 | - "subvol=data" 103 | - "compress=zstd" 104 | - "defaults" 105 | - mountpoint: "/mnt/disks/disk3" 106 | device: "/dev/disk/by-id/ata-WDC_WD180EDGZ-11B9PA0_2GH0M6HS-part1" 107 | fstype: "btrfs" 108 | options: 109 | - "subvol=data" 110 | - "compress=zstd" 111 | - "defaults" 112 | 113 | # nix-manager configuration 114 | nix_user: "root" 115 | nix_group: "root" 116 | nix_home: "/root" 117 | install_home_manager: true 118 | git_username: "Alex Kretzschmar" 119 | git_email: "{{ vault_cloudflare_account_email }}" 120 | -------------------------------------------------------------------------------- /group_vars/secrets.yaml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 62346235643835373565393961656664666462633739623238346137383734376136666337653433 3 | 6161626337626238643539323638643939616137646666370a653165363663626136623637386263 4 | 63656637303335653866303636353431343165396361316663653361373535656630306534653036 5 | 6238386237616532390a396331613566346366613435366439383934396637356239663333386562 6 | 66623830643031353337383137333436393638666266363335346539626335373339393563383333 7 | 65313835353361666564356162343036656531393831343966626462393538353331643137643133 8 | 62353263306633643964346535636564366136343266653431623231303037386236373662666564 9 | 37653936643362643930366531343266363065626333663536343038383264393837643362343331 10 | 62373264313362396430326336353639356334396530393434396530393838646239633130333539 11 | 31646465323866656466343738376466316231623636323630663839613832336133663939396530 12 | 33613161383936323862313934333766623163323465653932636232383733306636656661623432 13 | 63613162356561326232653430616330326136396431666532303738323732623035343562626261 14 | 30303536333634383163353233393236356266373161383966626234316664636463653636326663 15 | 65646235666161356435326262346461623565323631343534656632376633353739386531386334 16 | 63333564383834623533613163383265633330363338613462333138313666323630646462373462 17 | 35313061376337643261613937333865656366656632376131646336643535646537383961616132 18 | 33373631323039303034646163633061353630646433613832323265323335326136656261383730 19 | 32663739646431643332363737323566383434353535343234343136383933656464633838373835 20 | 61656531653866643061393464383061306165646165623066633937643433323862636331363162 21 | 30383262336363323666386233343738626636616663643339336666396538316465323265393939 22 | 33363137346130666531383162633964393666313639613264626263643661383536393833653161 23 | 31653334363932346534626330623530303262613265626364356238316139343661633833366263 24 | 31333566363764653161323063356663356163663132353264666665396366353535353563396639 25 | 37396435393466323362666665383865386635393732666564666462373838656137363635376666 26 | 37653032663062373230373831386237346265653063383366393138313563663432616335633731 27 | 64633038323665353536383763346161663039653961356537386233383032313435323562666464 28 | 35373666636233356137316535383062316635326165313966383863346164666135323663643335 29 | 65313037613564623032663262353362613663343331363334386165313835623234363835626630 30 | 64323034633861376132396235326365383333653435386665613532363133653430643764383263 31 | 63343434393836366631653435666462323766623939613865346431376538633563306662626638 32 | 34333332386263326631633164343362343862643764396438306432353433323538313538313765 33 | 66656339636338623434363765366337383132616637653662666539626232383864636232396461 34 | 65323463626466613533313434666564386366663866313662383162363639653033343464643762 35 | 32376335656230383535343539366531623866313561346532646563326530653530336332363263 36 | 33333962656434666436616363623461343032383762616661323437363765353034346136316333 37 | 63356133626133326131366230663166336465656332306165306563363331633439353835633866 38 | 30653936373233336133633966346534393765373337343736636666616561376331346433396538 39 | 32353230663436323239366134383237653833373930353265336564663665316463336338366136 40 | 65323762306332363736383438613764326138366166373161356134663437306137333339656164 41 | 32356636623938336266613263386363323737356138663464313761336537613130653934366662 42 | 33643366343030366337636332356465323735356337363639626339303732323763366264313535 43 | 62306433373431336337313935346235356562643637333035356163306433383666316266623662 44 | 38306565653563386137343239636631386630303365323231643461386164643566353332343431 45 | 31306263396131383364393538316636643662363037613264636366653634633439376362656235 46 | 32356563303336393038656238653238643865653230383439623463656564613130643535616434 47 | 30303232653636633564373139623561303135613462633864376363663632393334326230656131 48 | 38383536323365613832313830313965666332373666306336326262666466363339393838303138 49 | 39386436356263343561663963363762393664326135616433653636336362313630366264636665 50 | 33393065636330366638343538653837326136373532616432366331333063333932626331303465 51 | 64326233306137366236613966363265303366333265353531653635666130363531663662386563 52 | 30623030623535376536386632663530636332343133623163303137626164353937636263623539 53 | 33393737326435663732363635373266623030643531623332336539353761346130396164333632 54 | 34306234333033366338623436383938653466623336623131303864613339353233613833646263 55 | 62386131663761373733393166643038393562326132636666386538623763633231626338653464 56 | 30303865326439373164333661336465306639663530336363646236323739383733666639653062 57 | 35313736363062313838393165646339313134333666343131346266656365643738346161663637 58 | 30633234633135636439333230646432386431376163333338643865646366623732646635326562 59 | 38643236643036326132373233393763626331326637383533336434626638656336393138323834 60 | 39346266306536323565633465326137316562333865663465386661663038616163343232316338 61 | 31353766323037643635346164303633396537643964623262393332373636616530383735656363 62 | 39656334656437616261646161353131626138306438343136323830326462626439663963646633 63 | 61633135313961313263313164613465373231633464373135343031353861393166323966633734 64 | 37623736346565633133303739386363393365633561623438323339353561343833326134363061 65 | 34633737336163393333393936366363376463333137393962393738643864306434646166626266 66 | 62663637653964363535393439666262386566363163343163636563336335613639386332356330 67 | 66323135366432343265303264613638633935623962383035313262653733386462373035653535 68 | 36386130303934333266643566663033313565666465646633623930643663623465646166633332 69 | 64373136323165653935636130353663343130333032373138616338653765303137363139633830 70 | 33336238386533313165666664333463633538373534653638376137633066323466373335613939 71 | 35663535326664613065663138343731623230323239366261623830356130363532363864336337 72 | 38353038646162386530356536333936336162373438613432626331393533636330313564336138 73 | 37636164613331653065613764303035316161633565306439323235316332313235386535666631 74 | 66633533663466383131386434353562653831663636343961306532333433396662386336383164 75 | 63303338363734616632663365353365366233356530653736313635386261636535353030656465 76 | 30653730653637303264616437376330336466616366633065366530316261313466613363366332 77 | 37346636303039336138376635623065396166343863336230623731643138663462326364393838 78 | 66323738386235663035313264623633366161343834316465366139636538373361643033366265 79 | 64303030626636383466336236323562363132383434366536356434313963643636356261643465 80 | 34643136303632346265653437336662323263653435643264303334323063613462323037383038 81 | 36653666303861366366643634646463393034623333636265353437313462343264386338643032 82 | 32373339636330653536313165366531366636636164303730626664313538396632656235663130 83 | 32653333626563383766656331363835336434306366346138333935633839643830613639373730 84 | 32333362373966666261633234353466313834333765393232316231333236613563363935646466 85 | 38336533383036646131343134656138353839663233643438656639666264396361326436383430 86 | 61643933303337303231366436643432323338333464383562353630336232323861326162343438 87 | 61613761323234316135363035373564383030366637303235393832376261633265333962326233 88 | 33633037376565633430386366643734666361306461613333616633353937313839616434613238 89 | 32373062663230623161306635663337323663353566313734303466316635353063663562626262 90 | 64623838666530303037363665646532376361313337323130393663356137306337663362316433 91 | 32366531333135363961653732623334303533393731396339386563623439373862343431343037 92 | 61643965326565393961666562356665653431336434633531336430346338343038386166373039 93 | 65653833333836346134363264396235366563303732303037613339316266623937393035333533 94 | 37316234656435316661626234323931653139366132656439623962356562636535663064613165 95 | 33653631633433663563646363353363636561623434316162393337363163316461666461633366 96 | 63373161303336386662323030623035333737373938313362656130303237656136656361376133 97 | 65353730333536393637313935383638613265363539343161396563623936386337623839623833 98 | 30336462396639363133363034653634316566366564663162393466643464653662396532346633 99 | 61356566636662623063666562626132656363376164356532343236306165333463636265663964 100 | 30663663343533366139653937663233336264356435643532393231643261323336326635303337 101 | 62306236336231386537623934613264383666353262626538613561653634333564313435386535 102 | 65653339373034336362616161323338653666356232653637393334383064313232356362633965 103 | 32373233376238326531653366313230316565303735323135623461623030373764336264383239 104 | 61333162616262623736613664353365376262646663333938313366393061376539333838663163 105 | 63613637383537343439353365636631326632393462316666393136643034383766373331316362 106 | 66623832383830373332646135393339333937303462613839613334343737663032323264306338 107 | 36393838323337383966626638376531643934656361613732383036643038656635613034636663 108 | 30666364346130393762616562323162393166346432363234643365303332356336396464303364 109 | 30646465653130326139626633623034386663653238666464313735613330636134366533313830 110 | 34326635646636616661393530326264373637396262313237313638636531306134363033643862 111 | 35393037643561613465306631376265316166653763626430303262383533376266353930303939 112 | 37663838393039366137306631373337393437656536363936376233333931636539386465336235 113 | 34353533323332656634343764333565656463376263666630313161656664626265303439666430 114 | 38346138353839383263316261646363316136666362653765393932326336353031346162623865 115 | 65323066353737383031333634336537623663653132623632303165616564306561303837643432 116 | 38366338366138663739336139386239663062626431396137666239363636366434343565656537 117 | 33633037383134643161656166633133396436386338303661643434393163363963653462326239 118 | 64633236616536373130653033336136653730636662303831313037633666333662346661613365 119 | 37386365393362653866343463313435386338303962343636643662363536333762353939633932 120 | 65336363623465633663633630323835323534626562393239383634616366323530383466366633 121 | 36633536643963313538663535393962363966373039376138326261613534373438613563363766 122 | 61633736333138333539333739393334646362363433626432613837643263663963656234313636 123 | 32376437646565313163316663653033346638366437663536323062653932363437353235356661 124 | 37316463313166666665376361313834303536646431303762303932383837336262356362643035 125 | 62613833366165643762663337646331626432306430613532383738653333666564616632393934 126 | 33636632306338363937373933333631336530303566353963616364616363643262306366333933 127 | 31323263626334326162623834666330653331396336663635636366623634313632323964356563 128 | 66326433303965623531363238383238333231623462623766326138333661383433636662393136 129 | 64333138636330313464383764353831653738366436336562336131653131383831306565366238 130 | 66363936366662353637316236316238663838343564393631373063323837613839386231353665 131 | 39396336336334353435303239303532396664306132363463636564343634653235323737303836 132 | 38356134356130373363633239316466366231323332623066343030313237303332653236613639 133 | 63356536616632343936316139623734666363346162663736313263356261393664313763316339 134 | 31306465393430643963353835363930303739636439336433376665376434343564613230316466 135 | 38393133333333313433326562313631396636346164373539316634343161306239343962383738 136 | 30343565613064616430383962373831323362326333616261393632306262636564366165383363 137 | 32663234303662623065303031346635623331336334363133656639353066353862653936343863 138 | 33393164333332623638356564343433666631383438343061343466666563346435326361353332 139 | 61633366343236363137326232306161366363623637316431333066396137336532636463306331 140 | 30333931373830363764666361663335633563656530376533633030306465316231633965646561 141 | 34626333626266653435633234623666316331653530623531326466353731336536623032363130 142 | 63303034653739643032633136633132626264663361313131663233363962326637336437643430 143 | 65326164653235623863303333353864336631653863313733383763346236306463623336616631 144 | 38643836373864353764323262313433303235303062333063623566386131383133333938343362 145 | 30383733393863363564383037376562313330343438376463653130613261373232306130626233 146 | 65343931663966333566656263303966333632363131333335383938613163653731616163613366 147 | 64393364343561626232663537613234356465613437623333393838353836333534623134646631 148 | 38363264663934616533373037333932396134303364373032633666346636343339343631616438 149 | 34643734623363636163346465386332326430653132353563383833383433343539366537653133 150 | 31373331653230623733336634316266326134383236346435316564313131336164663365633033 151 | 65383263663439613333633434373838393061336636646661643639353066396261386662306635 152 | 39633438636135346531663861626265386336613764356635353363356235343833396336306137 153 | 37373966333264663234383864313465653332326232363463303037616261616531333032656636 154 | 31313863666236616232616438346330613338353264373763653163316630373831666633653465 155 | 30616234346534393261353936393836303439336132333463303364323234353634613236633664 156 | 38303464633439383031373663633839396533636432343835626163346638323365396633643765 157 | 61373335373361633435646533633637636238326664383862383564366533323666393439383536 158 | 61626338633336626136343131373965656238646361366635616166386464333139656662636237 159 | 64363833386131323632346238346234616233383436396462633339363232316433303038373531 160 | 363737653935313665663262386264323337 161 | -------------------------------------------------------------------------------- /hosts.ini: -------------------------------------------------------------------------------- 1 | ## CORE INFRA 2 | [ktz-core] 3 | ktz-core.ktz.ts.net ansible_ssh_user=root 4 | 5 | [neo] 6 | neo.ktz.ts.net ansible_ssh_user=root 7 | 8 | [caddy] 9 | # uses caddy hostname for ts-ssh 10 | caddy.ktz.ts.net ansible_ssh_user=root 11 | 12 | [wd-ts-sr] 13 | wd-ts-sr.ktz.ts.net ansible_ssh_user=root 14 | 15 | ## SERVERS 16 | 17 | [proxmorph] 18 | proxmorph ansible_ssh_user=root 19 | 20 | [c137] 21 | c137 ansible_ssh_user=root 22 | 23 | [pneuma] 24 | pneuma ansible_ssh_user=root 25 | 26 | [ms01] 27 | ms01 ansible_ssh_user=root 28 | 29 | ## LXCs 30 | [lxc-id] 31 | id ansible_ssh_user=root 32 | 33 | [lxc-ollama] 34 | ollama ansible_ssh_user=root 35 | 36 | [lxc-immich-app] 37 | immich-app ansible_ssh_user=root 38 | 39 | [lxc-immich-ml] 40 | immich-ml ansible_ssh_user=root 41 | 42 | [lxc-apps] 43 | apps ansible_ssh_user=root 44 | 45 | ## EXTERNAL 46 | 47 | [ktz-cloud] 48 | ktz-cloud ansible_ssh_user=ironicbadger 49 | 50 | [maple-apps] 51 | maple-apps ansible_ssh_user=root 52 | 53 | ## TAILSCALE LAB 54 | 55 | [clarkson] 56 | 10.42.37.10 ansible_ssh_user=root -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S just --justfile 2 | 3 | # Ansible playbook against specific host 4 | run HOST *TAGS: 5 | ansible-playbook -b run.yaml --limit {{HOST}} {{TAGS}} 6 | 7 | # docker compose against remote host via Ansible 8 | compose HOST *V: 9 | ansible-playbook run.yaml --limit {{HOST}} --tags compose {{V}} 10 | 11 | ########## 12 | ## repo stuff 13 | 14 | # updates submodules 15 | sub-update: 16 | #git submodule update --init --recursive 17 | git submodule update --remote --recursive 18 | 19 | # git submodule - repo URL + optional local folder name 20 | sub-add URL *NAME: 21 | #!/usr/bin/env sh 22 | if [ -z "{{NAME}}" ]; then 23 | # Extract repo name from URL if no name provided 24 | basename=$(basename "{{URL}}" .git) 25 | git submodule add {{URL}} "roles/${basename}" 26 | git submodule update --init --recursive 27 | git add .gitmodules "roles/${basename}" 28 | git commit -m "Adds ${basename} as a submodule" 29 | else 30 | git submodule add {{URL}} "roles/{{NAME}}" 31 | git submodule update --init --recursive 32 | git add .gitmodules "roles/{{NAME}}" 33 | git commit -m "Adds {{NAME}} as a submodule" 34 | fi 35 | 36 | # optionally use --force to force reinstall all requirements 37 | reqs *FORCE: 38 | ansible-galaxy install -r requirements.yaml {{FORCE}} 39 | 40 | # just vault (encrypt/decrypt/edit) 41 | vault ACTION: 42 | EDITOR='code --wait' ansible-vault {{ACTION}} group_vars/secrets.yaml -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "baseBranches": ["main"], 7 | "enabled": true 8 | } 9 | -------------------------------------------------------------------------------- /roles/local-neo-yeet/tasks/file-sharing.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: ensure cifs packages are present 4 | ansible.builtin.package: 5 | name: cifs-utils 6 | state: present 7 | 8 | - name: ensure mount point exists for samba shares 9 | ansible.builtin.file: 10 | path: "{{ item.mount_point }}" 11 | state: directory 12 | loop: "{{ samba_mounts }}" 13 | when: samba_mounts is defined 14 | no_log: true 15 | 16 | - name: mount samba shares 17 | ansible.builtin.mount: 18 | path: "{{ item.mount_point }}" 19 | src: "{{ item.share }}" 20 | fstype: cifs 21 | opts: "username={{ item.username }},password={{ item.password }},rw,uid={{ item.uid }},gid={{ item.gid }},nobrl" 22 | state: mounted 23 | loop: "{{ samba_mounts }}" 24 | when: samba_mounts is defined 25 | no_log: true -------------------------------------------------------------------------------- /roles/local-neo-yeet/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: import user tasks 4 | import_tasks: users.yaml 5 | 6 | - name: import file sharing tasks 7 | import_tasks: file-sharing.yaml -------------------------------------------------------------------------------- /roles/local-neo-yeet/tasks/users.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Add the user 4 | ansible.builtin.user: 5 | name: "{{ primary_user.name }}" 6 | uid: "{{ primary_user.uid }}" 7 | groups: "{{ primary_user.groups }}" 8 | shell: "{{ primary_user.shell }}" 9 | append: true -------------------------------------------------------------------------------- /roles/nix-manager/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | nix_user: "root" 3 | nix_group: "root" 4 | nix_home: "/root" 5 | install_home_manager: true 6 | nix_config_path: "{{ nix_home }}/.config/home-manager" 7 | -------------------------------------------------------------------------------- /roles/nix-manager/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Nix is installed 3 | shell: | 4 | curl -L https://nixos.org/nix/install | sh -s -- --daemon 5 | args: 6 | creates: /nix 7 | environment: 8 | NIX_INSTALLER_NO_MODIFY_PROFILE: "1" 9 | become: yes 10 | 11 | - name: Create Nix config directory 12 | file: 13 | path: "{{ nix_home }}/.config/nix" 14 | state: directory 15 | mode: '0755' 16 | owner: "{{ nix_user }}" 17 | group: "{{ nix_group }}" 18 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 19 | 20 | - name: Enable flakes 21 | copy: 22 | dest: "{{ nix_home }}/.config/nix/nix.conf" 23 | content: | 24 | experimental-features = nix-command flakes 25 | mode: '0644' 26 | owner: "{{ nix_user }}" 27 | group: "{{ nix_group }}" 28 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 29 | 30 | - name: Create home-manager configuration directory 31 | file: 32 | path: "{{ nix_config_path }}" 33 | state: directory 34 | mode: '0755' 35 | owner: "{{ nix_user }}" 36 | group: "{{ nix_group }}" 37 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 38 | 39 | - name: Copy flake.nix file 40 | template: 41 | src: flake.nix.j2 42 | dest: "{{ nix_config_path }}/flake.nix" 43 | mode: '0644' 44 | owner: "{{ nix_user }}" 45 | group: "{{ nix_group }}" 46 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 47 | 48 | - name: Copy home.nix file 49 | template: 50 | src: home.nix.j2 51 | dest: "{{ nix_config_path }}/home.nix" 52 | mode: '0644' 53 | owner: "{{ nix_user }}" 54 | group: "{{ nix_group }}" 55 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 56 | 57 | - name: Install home-manager 58 | shell: | 59 | . {{ nix_home }}/.nix-profile/etc/profile.d/nix.sh 60 | nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager 61 | nix-channel --update 62 | nix-shell '' -A install 63 | args: 64 | creates: "{{ nix_home }}/.config/home-manager" 65 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 66 | become_user: "{{ nix_user }}" 67 | when: install_home_manager | bool 68 | 69 | - name: Apply home-manager configuration 70 | shell: | 71 | . {{ nix_home }}/.nix-profile/etc/profile.d/nix.sh 72 | cd {{ nix_config_path }} 73 | nix-shell '' -A install --command "home-manager switch -b backup" 74 | become: "{{ 'yes' if nix_user == 'root' else 'no' }}" 75 | become_user: "{{ nix_user }}" 76 | when: install_home_manager | bool 77 | -------------------------------------------------------------------------------- /roles/nix-manager/templates/flake.nix.j2: -------------------------------------------------------------------------------- 1 | { 2 | description = "Home Manager configuration for {{ nix_user }}"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | home-manager = { 7 | url = "github:nix-community/home-manager"; 8 | inputs.nixpkgs.follows = "nixpkgs"; 9 | }; 10 | }; 11 | 12 | outputs = { nixpkgs, home-manager, ... }: 13 | let 14 | system = "x86_64-linux"; 15 | pkgs = nixpkgs.legacyPackages.${system}; 16 | in { 17 | homeConfigurations.{{ nix_user }} = home-manager.lib.homeManagerConfiguration { 18 | inherit pkgs; 19 | modules = [ ./home.nix ]; 20 | }; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /roles/nix-manager/templates/home.nix.j2: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | 3 | let 4 | system = pkgs.stdenv.hostPlatform.system; 5 | nixpkgs-unstable = import (fetchTarball { 6 | url = "https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz"; 7 | sha256 = "0mmcni35fxs87fnhavfprspczgnnkxyizy8a4x57y98y76c4q4da"; 8 | }) { 9 | inherit system; 10 | config.allowUnfree = true; 11 | }; 12 | in 13 | { 14 | home.username = "{{ nix_user }}"; 15 | home.homeDirectory = "{{ nix_home }}"; 16 | home.stateVersion = "23.11"; 17 | 18 | nixpkgs.config.allowUnfree = true; 19 | 20 | home.packages = with pkgs; [ 21 | {% for package in nix_packages %} 22 | {{ package }} 23 | {% endfor %} 24 | ]; 25 | 26 | # Programs managed by home-manager 27 | programs.git = { 28 | enable = true; 29 | userName = "{{ git_username }}"; 30 | userEmail = "{{ git_email }}"; 31 | }; 32 | 33 | programs.bash = { 34 | enable = true; 35 | shellAliases = { 36 | ll = "ls -la"; 37 | update = "home-manager switch"; 38 | }; 39 | }; 40 | 41 | programs.zsh = { 42 | enable = true; 43 | syntaxHighlighting.enable = true; 44 | autosuggestion.enable = true; 45 | oh-my-zsh = { 46 | enable = true; 47 | plugins = [ "git" "docker" "docker-compose" "zoxide" ]; 48 | theme = "robbyrussell"; 49 | }; 50 | }; 51 | 52 | programs.direnv = { 53 | enable = true; 54 | nix-direnv.enable = true; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /run.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ### CORE INFRA 3 | - hosts: ktz-core 4 | vars_files: 5 | - 'group_vars/secrets.yaml' 6 | vars: 7 | docker_compose_hostname: ktz-core 8 | appdata_path: /opt/appdata 9 | roles: 10 | - role: ironicbadger.figurine 11 | # pihole 12 | - role: ironicbadger.caddy 13 | tags: caddy 14 | - role: ironicbadger.docker-compose-generator 15 | tags: compose 16 | 17 | - hosts: neo 18 | vars_files: 19 | - 'group_vars/secrets.yaml' 20 | roles: 21 | - role: ironicbadger.figurine 22 | - role: ironicbadger.tailscale-routes-fix 23 | - role: ironicbadger.docker-compose-generator 24 | tags: compose 25 | 26 | - hosts: caddy 27 | vars_files: 28 | - 'group_vars/secrets.yaml' 29 | roles: 30 | - role: ironicbadger.figurine 31 | - role: ironicbadger.tailscale-routes-fix 32 | - role: ironicbadger.caddy 33 | tags: caddy 34 | 35 | - hosts: wd-ts-sr 36 | roles: 37 | - role: ironicbadger.figurine 38 | - role: ironicbadger.tailscale-routes-fix 39 | 40 | ### SERVERS 41 | - hosts: proxmorph 42 | vars_files: 43 | - 'group_vars/secrets.yaml' 44 | roles: 45 | - role: ironicbadger.figurine 46 | - role: ironicbadger.tailscale-routes-fix 47 | - role: ironicbadger.packages 48 | - role: nix-manager 49 | - role: ironicbadger.mounts 50 | - role: ironicbadger.docker-compose-generator 51 | tags: compose 52 | 53 | - hosts: c137 54 | vars_files: 55 | - 'group_vars/secrets.yaml' 56 | roles: 57 | - role: ironicbadger.figurine 58 | - role: ironicbadger.proxmox-host-nvidia-setup 59 | - role: ironicbadger.tailscale-routes-fix 60 | - role: ironicbadger.packages 61 | #- role: nix-manager 62 | - role: ironicbadger.mounts 63 | tags: mounts 64 | - role: ironicbadger.docker-compose-generator 65 | tags: compose 66 | 67 | - hosts: pneuma 68 | vars_files: 69 | - 'group_vars/secrets.yaml' 70 | roles: 71 | - role: ironicbadger.figurine 72 | - role: ironicbadger.tailscale-routes-fix 73 | 74 | - hosts: ms01 75 | vars_files: 76 | - 'group_vars/secrets.yaml' 77 | roles: 78 | - role: ironicbadger.figurine 79 | - role: ironicbadger.tailscale-routes-fix 80 | 81 | ### LXCs 82 | - hosts: id 83 | vars_files: 84 | - 'group_vars/secrets.yaml' 85 | vars: 86 | docker_compose_hostname: lxc-id 87 | roles: 88 | - role: ironicbadger.figurine 89 | - role: ironicbadger.docker-compose-generator 90 | tags: compose 91 | 92 | - hosts: immich-app 93 | vars_files: 94 | - 'group_vars/secrets.yaml' 95 | vars: 96 | docker_compose_hostname: lxc-immich-app 97 | appdata_path: /mnt/appdata 98 | roles: 99 | - role: ironicbadger.figurine 100 | - role: ironicbadger.docker-compose-generator 101 | tags: compose 102 | 103 | - hosts: immich-ml 104 | vars_files: 105 | - 'group_vars/secrets.yaml' 106 | vars: 107 | docker_compose_hostname: lxc-immich-ml 108 | roles: 109 | - role: ironicbadger.figurine 110 | - role: ironicbadger.docker-compose-generator 111 | tags: compose 112 | 113 | - hosts: ollama 114 | vars_files: 115 | - 'group_vars/secrets.yaml' 116 | vars: 117 | docker_compose_hostname: lxc-ollama 118 | appdata_path: /mnt/appdata 119 | roles: 120 | - role: ironicbadger.figurine 121 | - role: ironicbadger.docker-compose-generator 122 | tags: compose 123 | 124 | - hosts: apps 125 | vars_files: 126 | - 'group_vars/secrets.yaml' 127 | vars: 128 | docker_compose_hostname: lxc-apps 129 | appdata_path: /mnt/appdata 130 | roles: 131 | - role: ironicbadger.figurine 132 | - role: ironicbadger.docker-compose-generator 133 | tags: compose 134 | 135 | # OFFSITE 136 | 137 | ## vps 138 | - hosts: ktz-cloud 139 | vars_files: 140 | - 'group_vars/secrets.yaml' 141 | roles: 142 | - role: ironicbadger.docker-compose-generator 143 | tags: compose 144 | 145 | ## maple 146 | - hosts: maple-apps 147 | vars_files: 148 | - 'group_vars/secrets.yaml' 149 | vars: 150 | docker_compose_hostname: maple-apps 151 | appdata_path: /opt/appdata 152 | roles: 153 | - role: ironicbadger.figurine 154 | - role: ironicbadger.docker-compose-generator 155 | tags: compose -------------------------------------------------------------------------------- /services/c137/01-traefik/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | tr: 3 | image: traefik 4 | container_name: tr 5 | volumes: 6 | - "{{ appdata_path }}/apps/traefik/letsencrypt:/letsencrypt" 7 | - /var/run/docker.sock:/var/run/docker.sock:ro 8 | labels: 9 | - traefik.enable=false 10 | ports: 11 | - 80:80 12 | - 443:443 13 | environment: 14 | - CF_DNS_API_TOKEN={{ vault_cloudflare_dns_api_token }} 15 | command: 16 | - --log.level=info 17 | - --accesslog=false 18 | - --api.insecure=true 19 | - --providers.docker=true 20 | - --providers.docker.exposedbydefault=false 21 | - --entrypoints.web.address=:80 22 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 23 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 24 | - --entrypoints.websecure.address=:443 25 | - --entrypoints.websecure.http.tls.certresolver=cloudflare 26 | - --certificatesresolvers.cloudflare.acme.dnschallenge=true 27 | - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare 28 | - --certificatesresolvers.cloudflare.acme.email={{ vault_cloudflare_account_email }} 29 | - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json 30 | - --serversTransport.insecureSkipVerify=true 31 | restart: unless-stopped -------------------------------------------------------------------------------- /services/c137/02-mediaservers/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ## media servers 3 | plex: 4 | image: plexinc/pms-docker 5 | container_name: plex 6 | network_mode: host 7 | devices: 8 | - /dev/dri:/dev/dri 9 | volumes: 10 | - "{{ appdata_path }}/mediaservers/plex/config:/config" 11 | - /mnt/jbod:/data 12 | - /mnt/bigrust20:/mnt/bigrust20:ro 13 | # ram transcode 14 | - /dev/shm:/transcode 15 | labels: 16 | - traefik.enable=false 17 | environment: 18 | - "PUID={{ docker_compose_generator_uid }}" 19 | - "PGID={{ docker_compose_generator_gid }}" 20 | - "TZ={{ host_timezone }}" 21 | restart: unless-stopped 22 | jellyfin: 23 | image: jellyfin/jellyfin 24 | container_name: jellyfin 25 | hostname: jellyfin 26 | devices: 27 | - /dev/dri:/dev/dri 28 | volumes: 29 | - "{{ appdata_path }}/mediaservers/jellyfin:/config" 30 | - "/mnt/jbod:/data:ro" 31 | - /mnt/downloads:/downloads:ro 32 | # ram transcode 33 | - /dev/shm:/transcode 34 | labels: 35 | - traefik.enable=true 36 | - "traefik.http.routers.jellyfin.rule=Host(`jf.wd.ktz.me`)" 37 | - traefik.http.services.jellyfin.loadbalancer.server.port=8096 38 | ports: 39 | - 2285:8096 40 | environment: 41 | - "PUID={{ docker_compose_generator_uid }}" 42 | - "PGID={{ docker_compose_generator_gid }}" 43 | - "TZ=US/Eastern" 44 | - "JELLYFIN_PublishedServerUrl=jf.wd.ktz.me" 45 | restart: unless-stopped -------------------------------------------------------------------------------- /services/c137/03-arrmatey/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # arrrrrrrrrrrrrrrrr 3 | prowlarr: 4 | image: lscr.io/linuxserver/prowlarr 5 | container_name: prowlarr 6 | volumes: 7 | - "{{ appdata_path }}/arrmatey/prowlarr:/config" 8 | labels: 9 | - traefik.enable=true 10 | - "traefik.http.routers.prowlarr.rule=Host(`prowlarr.{{ m_wd_domain_me }}`)" 11 | environment: 12 | - "PUID={{ docker_compose_generator_uid }}" 13 | - "PGID={{ docker_compose_generator_gid }}" 14 | - "TZ={{ host_timezone }}" 15 | restart: unless-stopped 16 | sabnzbd: 17 | image: lscr.io/linuxserver/sabnzbd 18 | container_name: sabnzbd 19 | volumes: 20 | - "{{ appdata_path }}/arrmatey/sabnzbd:/config" 21 | - "{{ download_path }}:/downloads" 22 | labels: 23 | - traefik.enable=true 24 | - "traefik.http.routers.sabnzbd.rule=Host(`sabnzbd.{{ m_wd_domain_me }}`)" 25 | environment: 26 | - "PUID={{ docker_compose_generator_uid }}" 27 | - "PGID={{ docker_compose_generator_gid }}" 28 | - "TZ={{ host_timezone }}" 29 | depends_on: 30 | - prowlarr 31 | restart: unless-stopped 32 | sonarr: 33 | image: lscr.io/linuxserver/sonarr 34 | container_name: sonarr 35 | volumes: 36 | - "{{ appdata_path }}/arrmatey/sonarr:/config" 37 | - "{{ download_path }}/complete/tv:/downloads/complete/tv" 38 | - "{{ storage_path }}/tv:/tv" 39 | labels: 40 | - traefik.enable=true 41 | - "traefik.http.routers.sonarr.rule=Host(`sonarr.{{ m_wd_domain_me }}`)" 42 | environment: 43 | - "PUID={{ docker_compose_generator_uid }}" 44 | - "PGID={{ docker_compose_generator_gid }}" 45 | - "TZ={{ host_timezone }}" 46 | depends_on: 47 | - prowlarr 48 | - sabnzbd 49 | restart: unless-stopped 50 | radarr: 51 | image: lscr.io/linuxserver/radarr 52 | container_name: radarr 53 | volumes: 54 | - "{{ appdata_path }}/arrmatey/radarr:/config" 55 | - "{{ download_path }}/complete/movies:/downloads/complete/movies" 56 | - /mnt/jbod/movies:/movies 57 | labels: 58 | - traefik.enable=true 59 | - "traefik.http.routers.radarr.rule=Host(`radarr.{{ m_wd_domain_me }}`)" 60 | environment: 61 | - "PUID={{ docker_compose_generator_uid }}" 62 | - "PGID={{ docker_compose_generator_gid }}" 63 | - "TZ={{ host_timezone }}" 64 | depends_on: 65 | - prowlarr 66 | - sabnzbd 67 | restart: unless-stopped 68 | huntarr: 69 | image: ghcr.io/plexguide/huntarr:latest 70 | container_name: huntarr 71 | volumes: 72 | - "{{ appdata_path }}/arrmatey/huntarr:/config" 73 | labels: 74 | - traefik.enable=true 75 | - "traefik.http.routers.huntarr.rule=Host(`huntarr.{{ m_wd_domain_me }}`)" 76 | environment: 77 | - "TZ={{ host_timezone }}" 78 | depends_on: 79 | - sonarr 80 | - radarr 81 | restart: unless-stopped 82 | jellyseerr: 83 | image: fallenbagel/jellyseerr:latest 84 | container_name: jellyseerr 85 | volumes: 86 | - "{{ appdata_path }}/arrmatey/jellyseerr:/app/config" 87 | labels: 88 | - traefik.enable=true 89 | - "traefik.http.routers.jellyseerr.rule=Host(`jellyseerr.{{ m_wd_domain_me }}`)" 90 | restart: unless-stopped -------------------------------------------------------------------------------- /services/c137/04-monitoring/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # monitoring 3 | smokeping: 4 | image: lscr.io/linuxserver/smokeping 5 | container_name: smokeping 6 | volumes: 7 | - "{{ appdata_path }}/apps/smokeping/config:/config" 8 | - "{{ appdata_path }}/apps/smokeping/data:/data" 9 | labels: 10 | - traefik.enable=true 11 | - "traefik.http.routers.smokeping.rule=Host(`smokeping.{{ m_wd_domain_me }}`)" 12 | environment: 13 | - PUID=1000 14 | - PGID=100 15 | - "TZ={{ host_timezone }}" 16 | hostname: morpheus 17 | restart: unless-stopped 18 | librespeed: 19 | image: lscr.io/linuxserver/librespeed 20 | container_name: librespeed 21 | labels: 22 | - traefik.enable=false 23 | ports: 24 | - 8008:80 25 | environment: 26 | - MODE=standalone 27 | restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/01-traefik/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | traefik: 3 | image: traefik 4 | container_name: tr 5 | volumes: 6 | - "{{ appdata_path }}/apps/traefik/letsencrypt:/letsencrypt" 7 | - /var/run/docker.sock:/var/run/docker.sock:ro 8 | ports: 9 | - 80:80 10 | - 443:443 11 | environment: 12 | - "CF_DNS_API_TOKEN={{ vault_cloudflare_dns_api_token }}" 13 | command: 14 | - --log.level=info 15 | - --accesslog=false 16 | - --api.insecure=false 17 | - --providers.docker=true 18 | - --providers.docker.exposedbydefault=false 19 | - --entrypoints.web.address=:80 20 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 21 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 22 | - --entrypoints.websecure.address=:443 23 | - --certificatesresolvers.cloudflare.acme.dnschallenge=true 24 | - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare 25 | - "--certificatesresolvers.cloudflare.acme.email={{ vault_cloudflare_account_email }}" 26 | - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json 27 | - --serversTransport.insecureSkipVerify=true 28 | restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/02-monitoring/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ## monitoring 3 | smokeping: 4 | image: lscr.io/linuxserver/smokeping 5 | container_name: smokeping 6 | volumes: 7 | - "{{ appdata_path }}/smokeping/config:/config" 8 | - "{{ appdata_path }}/smokeping/data:/data" 9 | labels: 10 | - traefik.enable=true 11 | - "traefik.http.routers.smokeping.rule=Host(`smokeping.hetz.{{ domain_cloud }}`)" 12 | environment: 13 | - "PUID={{ docker_compose_generator_uid }}" 14 | - "PGID={{ docker_compose_generator_gid }}" 15 | - "TZ={{ host_timezone }}" 16 | hostname: ktz-cloud 17 | restart: unless-stopped 18 | librespeed: 19 | image: lscr.io/linuxserver/librespeed 20 | container_name: librespeed 21 | ports: 22 | - 8008:80 23 | environment: 24 | - "PUID={{ docker_compose_generator_uid }}" 25 | - "PGID={{ docker_compose_generator_gid }}" 26 | - "TZ={{ host_timezone }}" 27 | - MODE=standalone 28 | - TELEMETRY=false 29 | restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/03-static-sites/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # static sites 3 | pms-wiki: 4 | image: ghcr.io/ironicbadger/pms-wiki:latest 5 | container_name: pms-wiki 6 | labels: 7 | - traefik.enable=true 8 | - traefik.http.routers.pmswiki.rule=Host(`perfectmediaserver.com`) 9 | - traefik.http.routers.pmswiki.entrypoints=websecure 10 | - traefik.http.routers.pmswiki.tls.certresolver=cloudflare 11 | restart: unless-stopped 12 | ktz-simplelinks: 13 | image: fascinated/simple-links:latest 14 | container_name: ktz-simplelinks 15 | volumes: 16 | - "{{ appdata_path }}/ktzme/config/config.yml:/usr/src/app/config.yml:ro" 17 | - "{{ appdata_path }}/ktzme/config/public:/usr/src/app/public:rw" 18 | labels: 19 | - traefik.enable=true 20 | - traefik.http.routers.ktzme.rule=Host(`alex.ktz.me`) 21 | - traefik.http.routers.ktzme.entrypoints=websecure 22 | - traefik.http.routers.ktzme.tls.certresolver=cloudflare 23 | - traefik.http.services.ktzme.loadbalancer.server.port=3000 24 | restart: unless-stopped 25 | ktz-systems: 26 | image: nginx 27 | container_name: ktz-systems 28 | volumes: 29 | - "{{ appdata_path }}/ktzsystems/public:/usr/share/nginx/html:ro" 30 | labels: 31 | - traefik.enable=true 32 | - traefik.http.routers.ktzsystems.rule=Host(`ktzsystems.com`) 33 | - traefik.http.routers.ktzsystems.entrypoints=websecure 34 | - traefik.http.routers.ktzsystems.tls.certresolver=cloudflare 35 | restart: unless-stopped 36 | alleycat: 37 | image: nginx 38 | container_name: alleycat 39 | volumes: 40 | - "{{ appdata_path }}/website-alleycat:/usr/share/nginx/html:ro" 41 | labels: 42 | - traefik.enable=true 43 | - traefik.http.routers.alleycat.rule=Host(`alleycatmusictuition.com`) 44 | - traefik.http.routers.alleycat.entrypoints=websecure 45 | - traefik.http.routers.alleycat.tls.certresolver=cloudflare 46 | restart: unless-stopped 47 | ktz-sh: 48 | image: nginx 49 | container_name: ktz-sh 50 | volumes: 51 | - "{{ appdata_path }}/ktz-sh/data:/usr/share/nginx/html:ro" 52 | - "{{ appdata_path }}/ktz-sh/config/nginx.conf:/etc/nginx/nginx.conf" 53 | - "{{ appdata_path }}/ktz-sh/config/mime.types:/etc/nginx/mime.types" 54 | - "{{ appdata_path }}/ktz-sh/config/default.conf:/etc/nginx/conf.d/default.conf" 55 | #- "{{ appdata_path }}/ktz-sh/config/conf.d/index.conf:/etc/nginx/conf.d/index.conf" 56 | labels: 57 | - traefik.enable=true 58 | - traefik.http.routers.sh.rule=Host(`sh.ktz.me`) 59 | - traefik.http.routers.sh.entrypoints=websecure 60 | - traefik.http.routers.sh.tls.certresolver=cloudflare 61 | restart: unless-stopped 62 | # ktz-webfinger: 63 | # image: nginx 64 | # container_name: ktz-webfinger 65 | # volumes: 66 | # - "{{ appdata_path }}/ktz-webfinger/data:/usr/share/nginx/html:ro" 67 | # labels: 68 | # - traefik.enable=true 69 | # - traefik.http.routers.webfinger.rule=Host(`ktz.me`) 70 | # - traefik.http.routers.webfinger.entrypoints=websecure 71 | # - traefik.http.routers.webfinger.tls.certresolver=cloudflare 72 | # restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/04-blogs/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # blogs 3 | ktz-blog: 4 | image: ghost:5-alpine 5 | container_name: ktz-blog 6 | volumes: 7 | - "{{ appdata_path }}/ktz-blog/app:/var/lib/ghost/content" 8 | labels: 9 | - traefik.enable=true 10 | - "traefik.http.routers.ktzblog.rule=Host(`{{ cloud_url_ktz_blog }}`)" 11 | - traefik.http.routers.ktzblog.entrypoints=websecure 12 | - traefik.http.routers.ktzblog.tls.certresolver=cloudflare 13 | - "traefik.http.routers.ktzblog-rss.rule=Host(`{{ cloud_url_ktz_blog }}`) && PathPrefix(`/rss`)" 14 | - traefik.http.routers.ktzblog-rss.entrypoints=websecure 15 | - traefik.http.routers.ktzblog-rss.tls.certresolver=cloudflare 16 | - traefik.http.routers.ktzblog-rss.middlewares=rss-cache-control 17 | - traefik.http.middlewares.rss-cache-control.headers.customresponseheaders.Cache-Control=public, max-age=600 18 | environment: 19 | - "url=https://{{ cloud_url_ktz_blog }}" 20 | - database__client=mysql 21 | - database__connection__host=ktz-blog-db 22 | - "database__connection__user={{ ktzblog_db_user }}" 23 | - "database__connection__password={{ ktzblog_db_pass }}" 24 | - database__connection__database=ghost 25 | restart: unless-stopped 26 | ktz-blog-db: 27 | image: mysql:8.4 28 | container_name: ktz-blog-db 29 | volumes: 30 | - "{{ appdata_path }}/ktz-blog/db:/var/lib/mysql" 31 | environment: 32 | - MYSQL_ROOT_PASSWORD={{ ktzblog_db_pass }} 33 | restart: unless-stopped 34 | ktz-blog-cat: 35 | image: ghost:5-alpine 36 | container_name: ktz-blog-cat 37 | volumes: 38 | - "{{ appdata_path }}/ktz-blog-cat/content:/var/lib/ghost/content" 39 | labels: 40 | - traefik.enable=true 41 | - traefik.http.routers.purrfectprose.rule=Host(`purrfectprose.com`) 42 | - traefik.http.routers.purrfectprose.entrypoints=websecure 43 | - traefik.http.routers.purrfectprose.tls.certresolver=cloudflare 44 | environment: 45 | - NODE_ENV=production 46 | - url=https://purrfectprose.com 47 | - database__client=mysql 48 | - database__connection__host=ktz-blog-cat-db 49 | - "database__connection__user={{ purrfect_db_user }}" 50 | - "database__connection__password={{ purrfect_db_pass }}" 51 | - database__connection__database=ghost 52 | depends_on: 53 | - ktz-blog-cat-db 54 | restart: unless-stopped 55 | ktz-blog-cat-db: 56 | image: mysql:8.4 57 | container_name: ktz-blog-cat-db 58 | volumes: 59 | - "{{ appdata_path }}/ktz-blog-cat/db:/var/lib/mysql" 60 | environment: 61 | - "MYSQL_ROOT_PASSWORD={{ purrfect_db_pass }}" 62 | restart: unless-stopped 63 | ktz-blog-mum: 64 | image: ghost:4-alpine 65 | container_name: ktz-blog-mum 66 | volumes: 67 | - "{{ appdata_path }}/ktz-blog-mum:/var/lib/ghost/content" 68 | labels: 69 | - traefik.enable=true 70 | - traefik.http.routers.mumblog.rule=Host(`{{ cloud_url_mum_blog }}`) 71 | - traefik.http.routers.mumblog.entrypoints=websecure 72 | - traefik.http.routers.mumblog.tls.certresolver=cloudflare 73 | environment: 74 | - NODE_ENV=production 75 | - "url=https://{{ cloud_url_mum_blog }}" 76 | restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/05-unifi/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # unifi 3 | ktz-unifi: 4 | image: lscr.io/linuxserver/unifi-network-application 5 | container_name: ktz-unifi 6 | volumes: 7 | - "{{ appdata_path }}/ktz-unifi-netapp/config:/config" 8 | labels: 9 | - traefik.enable=true 10 | - "traefik.http.routers.ubiq.rule=Host(`{{ cloud_url_ubiq }}`)" 11 | - traefik.http.routers.ubiq.entrypoints=websecure 12 | - traefik.http.routers.ubiq.tls=true 13 | - traefik.http.routers.ubiq.tls.certresolver=cloudflare 14 | - traefik.http.services.ubiq.loadbalancer.server.scheme=https 15 | - traefik.http.services.ubiq.loadbalancer.server.port=8443 16 | ports: 17 | - "{{ cloud_unifi_port_advertise }}:{{ cloud_unifi_port_advertise_int }}" 18 | - 8080:8080 19 | - 3478:3478/udp 20 | environment: 21 | - "PUID={{ docker_compose_generator_uid }}" 22 | - "PGID={{ docker_compose_generator_gid }}" 23 | - "TZ={{ host_timezone }}" 24 | - "MONGO_USER={{ unifi_mongo_db_user }}" 25 | - "MONGO_PASS={{ unifi_mongo_db_password }}" 26 | - "MONGO_DBNAME={{ unifi_mongo_db_name }}" 27 | - MONGO_HOST=ktz-unifi-db 28 | - MONGO_PORT=27017 29 | mem_limit: 2g 30 | restart: unless-stopped 31 | ktz-unifi-db: 32 | image: docker.io/mongo:7.0 33 | container_name: ktz-unifi-db 34 | logging: 35 | driver: none 36 | volumes: 37 | - "{{ appdata_path }}/ktz-unifi-netapp/db:/data/db" 38 | restart: unless-stopped -------------------------------------------------------------------------------- /services/ktz-cloud/06-apps/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # other apps 3 | ktz-pastebin: 4 | image: mkaczanowski/pastebin 5 | container_name: ktz-pastebin 6 | volumes: 7 | - "{{ appdata_path }}/ktz-pastebin:/var/lib/pastebin" 8 | labels: 9 | - traefik.enable=true 10 | - "traefik.http.routers.ktzpaste.rule=Host(`paste.{{ domain_cloud }}`)" 11 | - traefik.http.routers.ktzpaste.entrypoints=websecure 12 | - traefik.http.routers.ktzpaste.tls.certresolver=cloudflare 13 | - traefik.http.services.ktzpaste.loadbalancer.server.port=8000 14 | command: 15 | - --address 16 | - 0.0.0.0 17 | - --db 18 | - /var/lib/pastebin/ 19 | - --uri 20 | - https://paste.{{ domain_cloud }} 21 | restart: unless-stopped 22 | ktz-lychee: 23 | image: lycheeorg/lychee:v6.4.1 24 | container_name: ktz-lychee 25 | volumes: 26 | - "{{ appdata_path }}/ktz-lychee/app/config:/conf" 27 | - "{{ appdata_path }}/ktz-lychee/app/uploads:/uploads" 28 | - "{{ appdata_path }}/ktz-lychee/app/sym:/sym" 29 | - "{{ appdata_path }}/ktz-lychee/app/logs:/logs" 30 | - "{{ appdata_path }}/ktz-lychee/app/tmp:/lychee-tmp" 31 | labels: 32 | - traefik.enable=true 33 | - traefik.http.routers.ktzlychee.rule=Host(`gallery.ktz.cloud`) 34 | - traefik.http.routers.ktzlychee.tls.certresolver=cloudflare 35 | environment: 36 | - "PHP_TZ={{ host_timezone }}" 37 | - "APP_URL=https://gallery.ktz.cloud" 38 | - DB_CONNECTION=mysql 39 | - DB_HOST=ktz-lychee-db 40 | - DB_PORT=3306 41 | - "DB_USERNAME={{ lychee_mysql_user }}" 42 | - "DB_DATABASE={{ lychee_mysql_database }}" 43 | - "DB_PASSWORD={{ lychee_mysql_password }}" 44 | depends_on: 45 | - ktz-lychee-db 46 | restart: unless-stopped 47 | ktz-lychee-db: 48 | image: mariadb:10.8.2-focal 49 | container_name: ktz-lychee-db 50 | volumes: 51 | - "{{ appdata_path }}/ktz-lychee/db:/var/lib/mysql" 52 | environment: 53 | - "MYSQL_ROOT_PASSWORD={{ lychee_mysql_root_password }}" 54 | - "MYSQL_PASSWORD={{ lychee_mysql_password }}" 55 | - "MYSQL_DATABASE={{ lychee_mysql_database }}" 56 | - "MYSQL_USER={{ lychee_mysql_user }}" 57 | restart: unless-stopped 58 | ktz-pocketid: 59 | image: ghcr.io/pocket-id/pocket-id 60 | container_name: ktz-pocketid 61 | environment: 62 | - "APP_URL=https://id.{{ domain_me }}" 63 | - TRUST_PROXY=true 64 | - "MAXMIND_LICENSE_KEY={{ pocket_id_maxmind_key }}" 65 | labels: 66 | - traefik.enable=true 67 | - "traefik.http.routers.pocketid.rule=Host(`id.{{ domain_me }}`)" 68 | - traefik.http.routers.pocketid.tls.certresolver=cloudflare 69 | volumes: 70 | - "{{ appdata_path }}/pocket-id/data:/app/backend/data" 71 | restart: unless-stopped 72 | ktz-n8n: 73 | image: n8nio/n8n:latest 74 | container_name: ktz-n8n 75 | restart: unless-stopped 76 | volumes: 77 | - "{{ appdata_path }}/ktz-n8n:/home/node/.n8n" 78 | environment: 79 | - N8N_HOST=n8n.{{ domain_cloud }} 80 | - N8N_PORT=5678 81 | - N8N_PROTOCOL=https 82 | - N8N_ENCRYPTION_KEY={{ n8n_encryption_key }} 83 | - NODE_ENV=production 84 | - "TZ={{ host_timezone }}" 85 | labels: 86 | - traefik.enable=true 87 | - "traefik.http.routers.ktzn8n.rule=Host(`n8n.{{ domain_cloud }}`)" 88 | - traefik.http.routers.ktzn8n.entrypoints=websecure 89 | - traefik.http.routers.ktzn8n.tls.certresolver=cloudflare 90 | - traefik.http.services.ktzn8n.loadbalancer.server.port=5678 91 | ### 92 | ktz-hedgedoc: 93 | image: quay.io/hedgedoc/hedgedoc:latest 94 | container_name: ktz-hedgedoc 95 | restart: unless-stopped 96 | #volumes: 97 | #- "{{ appdata_path }}/ktz-hedgedoc/public:/hedgedoc/public" 98 | environment: 99 | - CMD_DB_URL=postgres://hedgedoc:{{ hedgedoc_db_password }}@ktz-hedgedoc-db:5432/hedgedoc 100 | - CMD_DOMAIN=md.{{ domain_cloud }} 101 | - CMD_URL_ADDPORT=false 102 | - CMD_PROTOCOL_USESSL=true 103 | - CMD_ALLOW_EMAIL_REGISTER=false 104 | - "CMD_TZ={{ host_timezone }}" 105 | ## auth 106 | - CMD_OAUTH2_PROVIDERNAME=PocketID 107 | - "CMD_OAUTH2_CLIENT_ID={{ hedgedoc_oauth_client_id }}" 108 | - "CMD_OAUTH2_CLIENT_SECRET={{ hedgedoc_oauth_secret }}" 109 | - CMD_OAUTH2_SCOPE=openid email profile 110 | - CMD_OAUTH2_TOKEN_AUTH_METHOD=client_secret_post 111 | - CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR=preferred_username 112 | - CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR=name 113 | - CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR=email 114 | - CMD_OAUTH2_USER_PROFILE_URL=https://id.{{ domain_me }}/api/oidc/userinfo 115 | - CMD_OAUTH2_TOKEN_URL=https://id.{{ domain_me }}/api/oidc/token 116 | - CMD_OAUTH2_AUTHORIZATION_URL=https://id.{{ domain_me }}/authorize 117 | depends_on: 118 | - ktz-hedgedoc-db 119 | labels: 120 | - traefik.enable=true 121 | - "traefik.http.routers.ktzhedgedoc.rule=Host(`md.{{ domain_cloud }}`)" 122 | - traefik.http.routers.ktzhedgedoc.entrypoints=websecure 123 | - traefik.http.routers.ktzhedgedoc.tls.certresolver=cloudflare 124 | - traefik.http.services.ktzhedgedoc.loadbalancer.server.port=3000 125 | ktz-hedgedoc-db: 126 | image: postgres:14-alpine 127 | container_name: ktz-hedgedoc-db 128 | restart: unless-stopped 129 | volumes: 130 | - "{{ appdata_path }}/ktz-hedgedoc/database:/var/lib/postgresql/data" 131 | environment: 132 | - POSTGRES_USER=hedgedoc 133 | - "POSTGRES_PASSWORD={{ hedgedoc_db_password }}" 134 | - POSTGRES_DB=hedgedoc 135 | 136 | -------------------------------------------------------------------------------- /services/ktz-cloud/07-search/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | searxng: 3 | container_name: searxng 4 | image: docker.io/searxng/searxng:latest 5 | restart: unless-stopped 6 | volumes: 7 | - "{{ appdata_path }}/ktz-searxng/config:/etc/searxng:rw" 8 | labels: 9 | - traefik.enable=true 10 | - "traefik.http.routers.ktzsearch.rule=Host(`{{ searxng_base_url }}`)" 11 | - traefik.http.routers.ktzsearch.entrypoints=websecure 12 | - traefik.http.routers.ktzsearch.tls.certresolver=cloudflare 13 | - traefik.http.services.ktzsearch.loadbalancer.server.port=8080 14 | environment: 15 | - "SEARXNG_BASE_URL=https://{{ searxng_base_url }}/" 16 | cap_drop: 17 | - ALL 18 | cap_add: 19 | - CHOWN 20 | - SETGID 21 | - SETUID 22 | logging: 23 | driver: "json-file" 24 | options: 25 | max-size: "1m" 26 | max-file: "1" 27 | redis: 28 | container_name: redis 29 | image: docker.io/valkey/valkey:8-alpine 30 | command: valkey-server --save 30 1 --loglevel warning 31 | restart: unless-stopped 32 | volumes: 33 | - valkey-data2:/data 34 | cap_drop: 35 | - ALL 36 | cap_add: 37 | - SETGID 38 | - SETUID 39 | - DAC_OVERRIDE 40 | logging: 41 | driver: "json-file" 42 | options: 43 | max-size: "1m" 44 | max-file: "1" 45 | 46 | volumes: 47 | valkey-data2: -------------------------------------------------------------------------------- /services/ktz-core/01-ingress/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # proxy 3 | tsdproxy: 4 | image: almeidapaulopt/tsdproxy:1 5 | container_name: tsdproxy 6 | volumes: 7 | - /var/run/docker.sock:/var/run/docker.sock 8 | - "{{ appdata_path }}/apps/tsdproxy/data:/data" 9 | - "{{ appdata_path }}/apps/tsdproxy/config:/config" 10 | restart: unless-stopped 11 | ports: 12 | - "8080:8080" 13 | -------------------------------------------------------------------------------- /services/ktz-core/02-apps/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # dashboard 3 | glance: 4 | image: glanceapp/glance 5 | container_name: glance 6 | volumes: 7 | - "{{ appdata_path }}/apps/glance/config:/app/config" 8 | labels: 9 | tsdproxy.enable: "true" 10 | tsdproxy.name: "core-glance" 11 | tsdproxy.container_port: 8080 12 | ports: 13 | - 8006:8080 14 | restart: unless-stopped 15 | # monitoring 16 | smokeping: 17 | image: lscr.io/linuxserver/smokeping:2.8.2-r3-ls130 18 | container_name: smokeping 19 | volumes: 20 | - "{{ appdata_path }}/apps/smokeping/config:/config" 21 | - "{{ appdata_path }}/apps/smokeping/data:/data" 22 | labels: 23 | tsdproxy.enable: "true" 24 | tsdproxy.name: "core-smokeping" 25 | tsdproxy.container_port: 80 26 | ports: 27 | - 8007:80 28 | environment: 29 | - "PUID={{ docker_compose_generator_uid }}" 30 | - "PGID={{ docker_compose_generator_gid }}" 31 | - "TZ={{ host_timezone }}" 32 | hostname: core 33 | restart: unless-stopped 34 | librespeed: 35 | image: lscr.io/linuxserver/librespeed:5.4.1-ls231 36 | container_name: librespeed 37 | labels: 38 | tsdproxy.enable: "true" 39 | tsdproxy.name: "core-librespeed" 40 | tsdproxy.container_port: 80 41 | ports: 42 | - 8008:80 43 | environment: 44 | - MODE=standalone 45 | restart: unless-stopped 46 | # collators 47 | freshrss: 48 | image: freshrss/freshrss:latest 49 | container_name: freshrss 50 | volumes: 51 | - "{{ appdata_path }}/apps/freshrss/data:/var/www/FreshRSS/data" 52 | - "{{ appdata_path }}/apps/freshrss/extensions:/var/www/FreshRSS/extensions" 53 | environment: 54 | - TZ={{ host_timezone }} 55 | - CRON_MIN=13,43 56 | - "BASE_URL=https://rss.{{ apps_domain_me }}" 57 | ports: 58 | - 8000:80 59 | restart: unless-stopped 60 | # media 61 | # audiobookshelf: 62 | # image: ghcr.io/advplyr/audiobookshelf:latest 63 | # container_name: audiobookshelf 64 | # volumes: 65 | # - "{{ appdata_path }}/apps/audiobookshelf/config:/config" 66 | # - "{{ appdata_path }}/apps/audiobookshelf/metadata:/metadata" 67 | # - "/mnt/moosezfs-audiobooks:/audiobooks" 68 | # labels: 69 | # tsdproxy.enable: "true" 70 | # tsdproxy.name: "books" 71 | # tsdproxy.container_port: 80 72 | # ports: 73 | # - 8009:80 74 | # environment: 75 | # - "AUDIOBOOKSHELF_UID={{ docker_compose_generator_uid }}" 76 | # - "AUDIOBOOKSHELF_GID={{ docker_compose_generator_gid }}" 77 | # - "TZ={{ host_timezone }}" 78 | # restart: unless-stopped 79 | -------------------------------------------------------------------------------- /services/lxc-apps/01-traefik/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | tr: 3 | image: traefik 4 | container_name: tr 5 | volumes: 6 | - "{{ appdata_path }}/apps/traefik/letsencrypt:/letsencrypt" 7 | - /var/run/docker.sock:/var/run/docker.sock:ro 8 | labels: 9 | - traefik.enable=false 10 | ports: 11 | - 80:80 12 | - 443:443 13 | environment: 14 | - CF_DNS_API_TOKEN={{ vault_cloudflare_dns_api_token }} 15 | command: 16 | - --log.level=info 17 | - --accesslog=false 18 | - --api.insecure=true 19 | - --providers.docker=true 20 | - --providers.docker.exposedbydefault=false 21 | - --entrypoints.web.address=:80 22 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 23 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 24 | - --entrypoints.websecure.address=:443 25 | - --entrypoints.websecure.http.tls.certresolver=cloudflare 26 | - --certificatesresolvers.cloudflare.acme.dnschallenge=true 27 | - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare 28 | - --certificatesresolvers.cloudflare.acme.email={{ vault_cloudflare_account_email }} 29 | - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json 30 | - --serversTransport.insecureSkipVerify=true 31 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-apps/02-apps/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | karakeep: 3 | image: ghcr.io/karakeep-app/karakeep:release 4 | container_name: karakeep 5 | volumes: 6 | - "{{ appdata_path }}/apps/karakeep/data:/data" 7 | labels: 8 | - traefik.enable=true 9 | - "traefik.http.routers.karakeep.rule=Host(`keep.apps.{{ wd_domain_me }}`)" 10 | environment: 11 | - MEILI_ADDR=http://karakeep-meilisearch:7700 12 | - BROWSER_WEB_URL=http://karakeep-chrome:9222 13 | - "NEXTAUTH_SECRET={{ hoarder_nextauth_secret }}" 14 | - "MEILI_MASTER_KEY={{ hoarder_nextauth_masterkey }}" 15 | - "NEXTAUTH_URL=https://keep.apps.wd.ktz.me" 16 | - "OPENAI_API_KEY={{ hoarder_openai_key }}" 17 | - DATA_DIR=/data 18 | - CRAWLER_STORE_SCREENSHOT=true 19 | - CRAWLER_FULL_PAGE_SCREENSHOT=true 20 | - CRAWLER_ENABLE_ADBLOCKER=true 21 | # auth 22 | - "OAUTH_WELLKNOWN_URL=https://id.wd.ktz.me/.well-known/openid-configuration" 23 | - "OAUTH_PROVIDER_NAME=pocketid" 24 | - "OAUTH_CLIENT_ID={{ karakeep_oauth_client_id }}" 25 | - "OAUTH_CLIENT_SECRET={{ karakeep_oauth_secret }}" 26 | #- "OAUTH_TIMEOUT=10000" 27 | - "OAUTH_SCOPE=openid email profile" 28 | restart: unless-stopped 29 | karakeep-chrome: 30 | image: gcr.io/zenika-hub/alpine-chrome:123 31 | container_name: karakeep-chrome 32 | command: 33 | - --no-sandbox 34 | - --disable-gpu 35 | - --disable-dev-shm-usage 36 | - --remote-debugging-address=0.0.0.0 37 | - --remote-debugging-port=9222 38 | - --hide-scrollbars 39 | restart: unless-stopped 40 | karakeep-meilisearch: 41 | image: getmeili/meilisearch:v1.13.3 42 | container_name: karakeep-meilisearch 43 | environment: 44 | - MEILI_NO_ANALYTICS=true 45 | - "MEILI_MASTER_KEY={{ hoarder_nextauth_masterkey }}" 46 | volumes: 47 | - "{{ appdata_path }}/apps/karakeep/meilisearch:/meili_data" 48 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-apps/99-testing/compose.yaml: -------------------------------------------------------------------------------- 1 | # services: 2 | # mazanoke: 3 | # container_name: mazanoke 4 | # image: ghcr.io/civilblur/mazanoke:latest 5 | # ports: 6 | # - "3474:80" -------------------------------------------------------------------------------- /services/lxc-id/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | tr: 3 | image: traefik 4 | container_name: tr 5 | volumes: 6 | - "/opt/appdata/traefik/letsencrypt:/letsencrypt" 7 | - /var/run/docker.sock:/var/run/docker.sock:ro 8 | labels: 9 | - traefik.enable=false 10 | ports: 11 | - 80:80 12 | - 443:443 13 | environment: 14 | - CF_DNS_API_TOKEN={{ vault_cloudflare_dns_api_token }} 15 | command: 16 | - --log.level=info 17 | - --accesslog=false 18 | - --api.insecure=true 19 | - --providers.docker=true 20 | - --providers.docker.exposedbydefault=false 21 | - --entrypoints.web.address=:80 22 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 23 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 24 | - --entrypoints.websecure.address=:443 25 | - --entrypoints.websecure.http.tls.certresolver=cloudflare 26 | - --certificatesresolvers.cloudflare.acme.dnschallenge=true 27 | - --certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare 28 | - --certificatesresolvers.cloudflare.acme.email={{ vault_cloudflare_account_email }} 29 | - --certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json 30 | - --serversTransport.insecureSkipVerify=true 31 | restart: unless-stopped 32 | pocketid: 33 | image: ghcr.io/pocket-id/pocket-id 34 | container_name: pocketid 35 | environment: 36 | - PUBLIC_APP_URL=https://id.wd.ktz.me 37 | - TRUST_PROXY=true 38 | - "MAXMIND_LICENSE_KEY={{ pocket_id_maxmind_key }}" 39 | labels: 40 | - traefik.enable=true 41 | - "traefik.http.routers.pocketid.rule=Host(`id.{{ wd_domain_me }}`)" 42 | volumes: 43 | - "/opt/appdata/pocket-id/data:/app/backend/data" 44 | # Optional healthcheck 45 | healthcheck: 46 | test: "curl -f http://localhost/health" 47 | interval: 1m30s 48 | timeout: 5s 49 | retries: 2 50 | start_period: 10s 51 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-id/karakeep.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ts-karakeep: 3 | image: tailscale/tailscale:latest 4 | container_name: ts-karakeep 5 | hostname: keep 6 | environment: 7 | - "TS_AUTHKEY={{ tailscale_oauth_client_secret }}" 8 | - "TS_EXTRA_ARGS=--advertise-tags=tag:container" 9 | - TS_STATE_DIR=/var/lib/tailscale 10 | - TS_USERSPACE=false 11 | devices: 12 | - /dev/net/tun:/dev/net/tun 13 | cap_add: 14 | - net_admin 15 | volumes: 16 | - "{{ appdata_path }}/apps/karakeep/ts-state:/var/lib/tailscale" 17 | restart: unless-stopped 18 | karakeep: 19 | image: ghcr.io/karakeep-app/karakeep:release 20 | container_name: karakeep 21 | volumes: 22 | - "{{ appdata_path }}/apps/karakeep/data:/data" 23 | network_mode: service:ts-karakeep 24 | environment: 25 | - MEILI_ADDR=http://karakeep-meilisearch:7700 26 | - BROWSER_WEB_URL=http://karakeep-chrome:9222 27 | - "NEXTAUTH_SECRET={{ hoarder_nextauth_secret }}" 28 | - "MEILI_MASTER_KEY={{ hoarder_nextauth_masterkey }}" 29 | - "NEXTAUTH_URL=https://keep.ktz.ts.net" 30 | - "OPENAI_API_KEY={{ hoarder_openai_key }}" 31 | - DATA_DIR=/data 32 | - CRAWLER_STORE_SCREENSHOT=true 33 | - CRAWLER_FULL_PAGE_SCREENSHOT=true 34 | - CRAWLER_ENABLE_ADBLOCKER=true 35 | # auth 36 | - "OAUTH_WELLKNOWN_URL=https://idp.ktz.ts.net/.well-known/openid-configuration" 37 | - "OAUTH_PROVIDER_NAME=tsidp" 38 | - "OAUTH_CLIENT_ID=unused" 39 | - "OAUTH_CLIENT_SECRET=unused" 40 | #- "OAUTH_TIMEOUT=10000" 41 | - "OAUTH_SCOPE=openid email profile" 42 | restart: unless-stopped 43 | karakeep-chrome: 44 | image: gcr.io/zenika-hub/alpine-chrome:123 45 | container_name: karakeep-chrome 46 | command: 47 | - --no-sandbox 48 | - --disable-gpu 49 | - --disable-dev-shm-usage 50 | - --remote-debugging-address=0.0.0.0 51 | - --remote-debugging-port=9222 52 | - --hide-scrollbars 53 | restart: unless-stopped 54 | karakeep-meilisearch: 55 | image: getmeili/meilisearch:v1.13.3 56 | container_name: karakeep-meilisearch 57 | environment: 58 | - MEILI_NO_ANALYTICS=true 59 | - "MEILI_MASTER_KEY={{ hoarder_nextauth_masterkey }}" 60 | volumes: 61 | - "{{ appdata_path }}/apps/karakeep/meilisearch:/meili_data" 62 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-immich-app/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ## immich 3 | immich-server: 4 | image: ghcr.io/immich-app/immich-server:release 5 | container_name: immich-server 6 | volumes: 7 | - /mnt/bigrust20/photos/alex/immich/uploads:/usr/src/app/upload 8 | - /mnt/bigrust20/photos/alex/images:/mnt/bigrust20/photos/alex/images:ro 9 | - /etc/localtime:/etc/localtime:ro 10 | ports: 11 | - 2283:2283 12 | environment: 13 | - IMMICH_VERSION=release 14 | - UPLOAD_LOCATION=./library 15 | - "DB_PASSWORD={{ immich_db_password }}" 16 | - "DB_USERNAME={{ immich_db_username }}" 17 | - "DB_DATABASE_NAME={{ immich_db_name }}" 18 | - "DB_DATA_LOCATION={{ immich_db_location }}" 19 | - "DB_HOSTNAME={{ immich_db_hostname}}" 20 | - REDIS_HOSTNAME=immich-redis 21 | depends_on: 22 | - immich-redis 23 | - immich-database 24 | healthcheck: 25 | disable: false 26 | restart: unless-stopped 27 | immich-redis: 28 | image: docker.io/valkey/valkey:8-bookworm@sha256:42cba146593a5ea9a622002c1b7cba5da7be248650cbb64ecb9c6c33d29794b1 29 | container_name: immich-redis 30 | restart: unless-stopped 31 | immich-database: 32 | image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0 33 | container_name: immich-database 34 | volumes: 35 | - "{{ appdata_path }}/databases/immich/db:/var/lib/postgresql/data" 36 | environment: 37 | - "POSTGRES_PASSWORD={{ immich_db_password }}" 38 | - "POSTGRES_USER={{ immich_db_username }}" 39 | - "POSTGRES_DB={{ immich_db_name }}" 40 | - "POSTGRES_INITDB_ARGS='--data-checksums'" 41 | healthcheck: 42 | test: >- 43 | pg_isready --dbname="{{ immich_db_name }}" --username="{{ immich_db_username }}" || exit 1; Chksum="$$(psql --dbname="{{ immich_db_name }}" --username="{{ immich_db_username }}" --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1 44 | interval: 5m 45 | start_interval: 30s 46 | start_period: 5m 47 | command: >- 48 | postgres -c shared_preload_libraries=vectors.so,vchord -c 'search_path="{{ immich_db_username }}", public, vectors' -c logging_collector=on -c max_wal_size=2GB -c shared_buffers=512MB -c wal_compression=on 49 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-immich-app/immich-app-lxc.conf: -------------------------------------------------------------------------------- 1 | arch: amd64 2 | cores: 20 3 | features: nesting=1 4 | hostname: immich-app 5 | memory: 16384 6 | net0: name=eth0,bridge=vmbr0,firewall=1,gw=10.42.0.254,hwaddr=BC:24:11:D6:E5:FD,ip=10.42.0.111/21,type=veth 7 | ostype: debian 8 | rootfs: nvme1tb:subvol-113-disk-0,size=64G 9 | swap: 512 10 | unprivileged: 0 11 | lxc.cgroup2.devices.allow: c 10:200 rwm 12 | lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 13 | lxc.mount.entry: /mnt/appdata/databases/immich mnt/appdata/databases/immich none bind,create=dir 0 0 14 | lxc.mount.entry: /mnt/bigrust20/photos mnt/bigrust20/photos none bind,create=dir 0 0 -------------------------------------------------------------------------------- /services/lxc-immich-ml/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # immich-ml 3 | immich-machine-learning: 4 | image: ghcr.io/immich-app/immich-machine-learning:release-cuda 5 | container_name: immich-machine-learning 6 | volumes: 7 | - "/opt/apps/immich/cache:/cache" 8 | ports: 9 | - 3003:3003 10 | dns: 11 | - 1.1.1.1 12 | deploy: 13 | resources: 14 | reservations: 15 | devices: 16 | - driver: nvidia 17 | count: 1 18 | capabilities: [gpu] 19 | restart: unless-stopped -------------------------------------------------------------------------------- /services/lxc-ollama/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # AI 3 | ollama: 4 | image: ollama/ollama 5 | container_name: ollama 6 | volumes: 7 | - "{{ appdata_path }}/apps/ollama:/root/.ollama" 8 | ports: 9 | - 11434:11434 10 | deploy: 11 | resources: 12 | reservations: 13 | devices: 14 | - driver: nvidia 15 | count: 1 16 | capabilities: [gpu] 17 | restart: unless-stopped 18 | openwebui: 19 | image: ghcr.io/open-webui/open-webui:main 20 | container_name: openwebui 21 | volumes: 22 | - "{{ appdata_path }}/apps/open-webui:/app/backend/data" 23 | ports: 24 | - 8080:8080 25 | environment: 26 | - OLLAMA_BASE_URL=http://ollama:11434 27 | restart: unless-stopped -------------------------------------------------------------------------------- /services/maple-apps/02-apps/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | freshrss: 3 | image: freshrss/freshrss:latest 4 | container_name: freshrss 5 | volumes: 6 | - "{{ appdata_path }}/freshrss/data:/var/www/FreshRSS/data" 7 | - "{{ appdata_path }}/freshrss/extensions:/var/www/FreshRSS/extensions" 8 | environment: 9 | - TZ={{ host_timezone }} 10 | - CRON_MIN=13,43 11 | - "BASE_URL=https://rss.{{ apps_domain_me }}" 12 | ports: 13 | - 8000:80 14 | restart: unless-stopped 15 | # ### 16 | # linkwarden: 17 | # env_file: .env 18 | # environment: 19 | # - DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres 20 | # restart: always 21 | # # build: . # uncomment to build from source 22 | # image: ghcr.io/linkwarden/linkwarden:latest # comment to build from source 23 | # ports: 24 | # - 3000:3000 25 | # volumes: 26 | # - ./data:/data/data 27 | # depends_on: 28 | # - postgres 29 | # - meilisearch 30 | # meilisearch: 31 | # image: getmeili/meilisearch:v1.12.8 32 | # restart: always 33 | # env_file: 34 | # - .env 35 | # volumes: 36 | # - ./meili_data:/meili_data 37 | # postgres: 38 | # image: postgres:16-alpine 39 | # env_file: .env 40 | # restart: always 41 | # volumes: 42 | # - ./pgdata:/var/lib/postgresql/data -------------------------------------------------------------------------------- /services/maple-apps/03-media/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | abs: 3 | image: advplyr/audiobookshelf 4 | container_name: abs 5 | volumes: 6 | - "/mnt/audiobooks/library:/audiobooks:ro" 7 | - "{{ appdata_path }}/audiobookshelf/metadata:/metadata" 8 | - "{{ appdata_path }}/audiobookshelf/config:/config" 9 | ports: 10 | - 2284:80 11 | restart: unless-stopped --------------------------------------------------------------------------------