Compare commits
15 commits
0af8ac86b9
...
aa53257d9b
Author | SHA1 | Date | |
---|---|---|---|
|
aa53257d9b | ||
f612b5de23 | |||
7083a81cd7 | |||
0bd79e32fb | |||
38e7aaf8ae | |||
51c1716f19 | |||
647b647eea | |||
52a9c55a88 | |||
a8b15ebfcb | |||
|
2c0e68ef9c | ||
|
1ee1b8e07b | ||
|
3fd1bb9044 | ||
|
311ee6355f | ||
|
6b600b6745 | ||
|
304f412950 |
11 changed files with 689 additions and 27 deletions
16
.forgejo/workflows/build-images.yaml
Normal file
16
.forgejo/workflows/build-images.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: "build demo images"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build NixOS images
|
||||||
|
runs-on: nixos
|
||||||
|
steps:
|
||||||
|
- name: install node
|
||||||
|
run: nix-env -iA nixpkgs.nodejs_20
|
||||||
|
- name: checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: run generator
|
||||||
|
run: nix-shell -p nixos-generators --run "nixos-generate -c inventory/demo-configuration.nix -f proxmox-lxc"
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.qcow2
|
||||||
|
result
|
||||||
node_modules
|
node_modules
|
||||||
cache
|
cache
|
||||||
dist
|
dist
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default defineConfig({
|
||||||
{
|
{
|
||||||
text: 'Getting started',
|
text: 'Getting started',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Generate configuration', link: '/generate' },
|
{ text: 'Local installation', link: '/installation' },
|
||||||
{ text: 'Build virtual machine', link: '/build-virtual-machine' },
|
{ text: 'Build virtual machine', link: '/build-virtual-machine' },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
19
build-virtual-machine.md
Normal file
19
build-virtual-machine.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Build images based on your generated configurations
|
||||||
|
|
||||||
|
Your Nix configurations are located in the `inventory` folder you can build and run the configuration file you want by running nixos-generate from the NixiN root folder. If the build succeeds, the last output is a command located in your nix store to run your vm.
|
||||||
|
|
||||||
|
## Images formats
|
||||||
|
|
||||||
|
Check <https://github.com/nix-community/nixos-generators?tab=readme-ov-file#supported-formats> to find available formats.
|
||||||
|
|
||||||
|
For example, for qemu-kvm runner
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nixos-generate -c inventory/demo-configuration.nix -f vm
|
||||||
|
```
|
||||||
|
|
||||||
|
(nixos-generate is installed by nix-shell, check `shell.nix` to see the packages added)
|
||||||
|
|
||||||
|
## Cross compile
|
||||||
|
|
||||||
|
Check <https://github.com/nix-community/nixos-generators?tab=readme-ov-file#cross-compiling>.
|
|
@ -1,10 +1,77 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
beforeMount() {
|
||||||
|
this.availableServices.forEach((s, i) => {
|
||||||
|
s.inBundle = []
|
||||||
|
})
|
||||||
|
this.availableBundles.forEach((b, i) => {
|
||||||
|
b.services.forEach((s) => {
|
||||||
|
this.availableServices.find(item => item.id === s).inBundle.push(b.id);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
netconf: 'autoconfig',
|
netconf: 'autoconfig',
|
||||||
networkingHostname: '',
|
networkingHostname: '',
|
||||||
networkingDomain: 'distrilab.org',
|
networkingDomain: 'distrilab.eu',
|
||||||
|
availableBundles: [{
|
||||||
|
"id": "writeCollectively",
|
||||||
|
"name": "Write collectively : pads",
|
||||||
|
"services": [
|
||||||
|
'hedgedoc', 'nextcloud'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forge",
|
||||||
|
"name": "Forge : git repo, CI/CD workers, and NixiN",
|
||||||
|
"services": [
|
||||||
|
'forgejo', 'forgejoRunner', 'nixin'
|
||||||
|
]
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "socialMedia",
|
||||||
|
"name": "Social media: hosted social medias in activitypub web-apps",
|
||||||
|
"services": [
|
||||||
|
'gotosocial', 'peertube', 'lemmy'
|
||||||
|
]
|
||||||
|
|
||||||
|
}],
|
||||||
|
availableServices: [{
|
||||||
|
"id": "hedgedoc",
|
||||||
|
"name": "Hedgedoc : realtime collaborative markdown editor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forgejo",
|
||||||
|
"name": "Forgejo : git hosting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "forgejoRunner",
|
||||||
|
"name": "Forgejo runner : CD/CI runner for Forgejo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gotosocial",
|
||||||
|
"name": "Gotosocial : personal light activityPub social media"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "peertube",
|
||||||
|
"name": "Peertube : video hosting platform with activityPub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lemmy",
|
||||||
|
"name": "Lemmy : reddit alternative with activityPub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nextcloud",
|
||||||
|
"name": "Nextcloud : personnal cloud"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "nixin",
|
||||||
|
"name": "NixiN : web ui for configurations"
|
||||||
|
}],
|
||||||
|
nixinBundles: [],
|
||||||
|
nixinServices: [],
|
||||||
timezone: 'Etc/UTC',
|
timezone: 'Etc/UTC',
|
||||||
locale: 'en_US.UTF-8',
|
locale: 'en_US.UTF-8',
|
||||||
user: 'operator',
|
user: 'operator',
|
||||||
|
@ -18,6 +85,13 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.networkingDomain = ''
|
this.networkingDomain = ''
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
selectServices(services) {
|
||||||
|
services.forEach((s) => {
|
||||||
|
if (this.nixinServices.indexOf(s) === -1) {
|
||||||
|
this.nixinServices.push(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,27 +105,23 @@ export default {
|
||||||
<strong>Choose your network configuration</strong>
|
<strong>Choose your network configuration</strong>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" v-model="netconf" name="netconf" value="autoconfig"
|
<input type="radio" v-model="netconf" name="netconf" value="autoconfig"
|
||||||
@click="netconfHasBeenChanged('autoconfig')">I'm a
|
@click="netconfHasBeenChanged('autoconfig')">I'm a noob in network config, I trust you to provide networking
|
||||||
noob in
|
for me (ipv6 only)</label>
|
||||||
network config, I trust
|
|
||||||
you to provide networking for me (ipv6 only)</label>
|
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" v-model="netconf" name="netconf" value="publicip" @click="netconfHasBeenChanged">My server
|
<input type="radio" v-model="netconf" name="netconf" value="publicip" @click="netconfHasBeenChanged">My server
|
||||||
has a public ip that I can
|
has a public ip that I can provide
|
||||||
provide
|
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" v-model="netconf" name="netconf" value="localnetwork" @click="netconfHasBeenChanged">My
|
<input type="radio" v-model="netconf" name="netconf" value="localnetwork" @click="netconfHasBeenChanged">My
|
||||||
router is set so that my local
|
router is set so that my local machine is accessible on the public network
|
||||||
machine is accessible on the public network
|
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" v-model="netconf" name="netconf" value="wireguard" @click="netconfHasBeenChanged">My server
|
<input type="radio" v-model="netconf" name="netconf" value="wireguard" @click="netconfHasBeenChanged">My server
|
||||||
can use a wireguard server i
|
can use a wireguard server i can configure
|
||||||
can configure
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-cell">
|
<div class="form-cell">
|
||||||
<label>Machine network name</label>
|
<label>Machine network name</label>
|
||||||
|
@ -68,14 +138,33 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Usage bundles</h2>
|
<h2>Usage bundles</h2>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-cell">
|
||||||
|
<strong>Choose your usage bundles (multiple choices possible if your machine can handle it)</strong>
|
||||||
|
<div v-for="bundle in availableBundles">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="nixinBundles" :id="bundle.id" :value="bundle.id"
|
||||||
|
@click="selectServices(bundle.services)" />
|
||||||
|
{{ bundle.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Applications packages</h2>
|
<h2>Services</h2>
|
||||||
|
<div v-if="nixinBundles.length === 0">👆 Choose any upper bundle to make associated services appear.</div>
|
||||||
|
<div v-for="service in availableServices">
|
||||||
|
<label v-if="service.inBundle.some(ai => nixinBundles.includes(ai))">
|
||||||
|
<input type="checkbox" v-model="nixinServices" :id="service.id" :value="service.id" />
|
||||||
|
{{ service.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Advanced configuration</h2>
|
<h2>Other configuration</h2>
|
||||||
Operating UNIX user name
|
Operating UNIX user name<br />
|
||||||
Operating UNIX user password
|
Operating UNIX user password<br />
|
||||||
Timezone
|
Timezone<br />
|
||||||
Locale
|
Locale<br />
|
||||||
|
|
||||||
<h2>Auto-generated configuration.nix file</h2>
|
<h2>Auto-generated configuration.nix file</h2>
|
||||||
<pre>
|
<pre>
|
||||||
|
@ -87,9 +176,6 @@ export default {
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = true;
|
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = "{{ networkingHostname }}";
|
hostName = "{{ networkingHostname }}";
|
||||||
domain = "{{ networkingDomain }}";
|
domain = "{{ networkingDomain }}";
|
||||||
|
@ -136,6 +222,453 @@ export default {
|
||||||
|
|
||||||
time.timeZone = "{{ timezone }}";
|
time.timeZone = "{{ timezone }}";
|
||||||
i18n.defaultLocale = "{{ locale }}";
|
i18n.defaultLocale = "{{ locale }}";
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('gotosocial')">
|
||||||
|
{
|
||||||
|
services.gotosocial = {
|
||||||
|
enable = true;
|
||||||
|
setupPostgresqlDB = true;
|
||||||
|
settings = {
|
||||||
|
application-name = "My GoToSocial";
|
||||||
|
host = "gotosocial.example.com";
|
||||||
|
protocol = "https";
|
||||||
|
bind-address = "127.0.0.1";
|
||||||
|
port = 8080;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
clientMaxBodySize = "40M";
|
||||||
|
virtualHosts = with config.services.gotosocial.settings; {
|
||||||
|
"${host}" = {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
proxyWebsockets = true;
|
||||||
|
proxyPass = "http://${bind-address}:${toString port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('peertube')">
|
||||||
|
networking.extraHosts = ''
|
||||||
|
127.0.0.1 peertube.local
|
||||||
|
'';
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
"peertube/password-posgressql-db".text = "test123";
|
||||||
|
"peertube/password-redis-db".text = "test123";
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
|
||||||
|
peertube = {
|
||||||
|
enable = true;
|
||||||
|
localDomain = "peertube.local";
|
||||||
|
enableWebHttps = false;
|
||||||
|
database = {
|
||||||
|
host = "127.0.0.1";
|
||||||
|
name = "peertube_local";
|
||||||
|
user = "peertube_test";
|
||||||
|
passwordFile = "/etc/peertube/password-posgressql-db";
|
||||||
|
};
|
||||||
|
redis = {
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = 31638;
|
||||||
|
passwordFile = "/etc/peertube/password-redis-db";
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
listen.hostname = "0.0.0.0";
|
||||||
|
instance.name = "PeerTube Test Server";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
postgresql = {
|
||||||
|
enable = true;
|
||||||
|
enableTCPIP = true;
|
||||||
|
authentication = ''
|
||||||
|
hostnossl peertube_local peertube_test 127.0.0.1/32 md5
|
||||||
|
'';
|
||||||
|
initialScript = pkgs.writeText "postgresql_init.sql" ''
|
||||||
|
CREATE ROLE peertube_test LOGIN PASSWORD 'test123';
|
||||||
|
CREATE DATABASE peertube_local TEMPLATE template0 ENCODING UTF8;
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE peertube_local TO peertube_test;
|
||||||
|
ALTER DATABASE peertube_local OWNER TO peertube_test;
|
||||||
|
\connect peertube_local
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS unaccent;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
redis.servers.peertube = {
|
||||||
|
enable = true;
|
||||||
|
bind = "0.0.0.0";
|
||||||
|
requirePass = "test123";
|
||||||
|
port = 31638;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('lemmy')">
|
||||||
|
let
|
||||||
|
|
||||||
|
# add nginx reverse proxy and ACME web certificate
|
||||||
|
add_nginx = true;
|
||||||
|
nginx_ports = [ 80 443 ];
|
||||||
|
|
||||||
|
lemmy = {
|
||||||
|
upstreamName = "lemmy";
|
||||||
|
dataDir = "/var/lib/lemmy";
|
||||||
|
ip = "127.0.0.1";
|
||||||
|
port = 1234;
|
||||||
|
# TODO: Change this domain to your own
|
||||||
|
domain = "lemmy.example.com";
|
||||||
|
};
|
||||||
|
|
||||||
|
lemmy-ui = {
|
||||||
|
upstreamName = "lemmy-ui";
|
||||||
|
ip = "127.0.0.1";
|
||||||
|
port = 8536;
|
||||||
|
};
|
||||||
|
|
||||||
|
pict-rs = {
|
||||||
|
ip = "127.0.0.1";
|
||||||
|
port = 8080;
|
||||||
|
};
|
||||||
|
|
||||||
|
acmeDomain = lemmy.domain;
|
||||||
|
nginxVhost = lemmy.domain;
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
security.acme = lib.mkIf add_nginx {
|
||||||
|
# TODO: change this to true if you accept
|
||||||
|
acceptTerms = false;
|
||||||
|
defaults = {
|
||||||
|
# TODO: you will receive a notification if automatic certificate renewal fails
|
||||||
|
email = "postmaster@${lemmy.domain}";
|
||||||
|
# TODO: put your dns provider here: https://go-acme.github.io/lego/dns/
|
||||||
|
dnsProvider = "";
|
||||||
|
# TODO: this file should contain environment variables expected by your dns provider
|
||||||
|
credentialsFile = "";
|
||||||
|
};
|
||||||
|
certs."${acmeDomain}" = {
|
||||||
|
domain = "${acmeDomain}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = lib.mkIf add_nginx nginx_ports;
|
||||||
|
|
||||||
|
# is needed because of certificate file permissions
|
||||||
|
users.users.nginx.extraGroups = lib.mkIf add_nginx ["acme"];
|
||||||
|
|
||||||
|
services.nginx = lib.mkIf add_nginx {
|
||||||
|
upstreams."${lemmy.upstreamName}".servers."${lemmy.ip}:${builtins.toString lemmy.port}" = {};
|
||||||
|
upstreams."${lemmy-ui.upstreamName}".servers."${lemmy-ui.ip}:${builtins.toString lemmy-ui.port}" = {};
|
||||||
|
|
||||||
|
virtualHosts."${nginxVhost}" = {
|
||||||
|
useACMEHost = "${acmeDomain}";
|
||||||
|
# inherit from config.security.acme.acmeRoot;
|
||||||
|
acmeRoot = null;
|
||||||
|
# add redirects from http to https
|
||||||
|
forceSSL = true;
|
||||||
|
# this whole block was lifted from
|
||||||
|
https://github.com/LemmyNet/lemmy/blob/ef1aa18fd20cc03d492a81cb70cc75cf3281649f/docker/nginx.conf#L21 lines
|
||||||
|
21-32
|
||||||
|
extraConfig = ''
|
||||||
|
# disables emitting nginx version on error pages and in the “Server” response header field
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/css application/javascript image/svg+xml;
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
# Upload limit, relevant for pictrs
|
||||||
|
client_max_body_size 20M;
|
||||||
|
|
||||||
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
'';
|
||||||
|
|
||||||
|
locations = {
|
||||||
|
"/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
# distinguish between ui requests and backend
|
||||||
|
# don't change lemmy-ui or lemmy here, they refer to the upstream definitions on top
|
||||||
|
set $proxpass "http://${lemmy-ui.upstreamName}";
|
||||||
|
|
||||||
|
if ($http_accept = "application/activity+json") {
|
||||||
|
set $proxpass "http://${lemmy.upstreamName}";
|
||||||
|
}
|
||||||
|
if ($http_accept = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
|
||||||
|
set $proxpass "http://${lemmy.upstreamName}";
|
||||||
|
}
|
||||||
|
if ($request_method = POST) {
|
||||||
|
set $proxpass "http://${lemmy.upstreamName}";
|
||||||
|
}
|
||||||
|
proxy_pass $proxpass;
|
||||||
|
|
||||||
|
# Cuts off the trailing slash on URLs to make them valid
|
||||||
|
rewrite ^(.+)/+$ $1 permanent;
|
||||||
|
|
||||||
|
# Send actual client IP upstream
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# again, lifted wholesale from
|
||||||
|
https://github.com/LemmyNet/lemmy/blob/ef1aa18fd20cc03d492a81cb70cc75cf3281649f/docker/nginx.conf#L60 lines
|
||||||
|
60-69 (nice!)
|
||||||
|
"~ ^/(api|pictrs|feeds|nodeinfo|.well-known)" = {
|
||||||
|
proxyPass = "http://${lemmy.upstreamName}";
|
||||||
|
extraConfig = ''
|
||||||
|
# proxy common stuff
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
## Send actual client IP upstream
|
||||||
|
#proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
#proxy_set_header Host $host;
|
||||||
|
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.lemmy-ui = {
|
||||||
|
environment = {
|
||||||
|
LEMMY_UI_HOST = lib.mkForce "${lemmy-ui.ip}:${toString lemmy-ui.port}";
|
||||||
|
LEMMY_UI_LEMMY_INTERNAL_HOST = lib.mkForce "${lemmy.ip}:${toString lemmy.port}";
|
||||||
|
LEMMY_UI_LEMMY_EXTERNAL_HOST = lib.mkForce lemmy.domain ;
|
||||||
|
LEMMY_UI_HTTPS="true";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.pict-rs = {
|
||||||
|
enable = true;
|
||||||
|
port = pict-rs.port;
|
||||||
|
dataDir = "${dataDir}/pict-rs";
|
||||||
|
address = pict-rs.ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.lemmy = {
|
||||||
|
requires = ["postgresql.service"];
|
||||||
|
after = ["postgresql.service"];
|
||||||
|
environment = {
|
||||||
|
LEMMY_DATABASE_URL = lib.mkForce "postgresql://lemmy@127.0.0.1:${toString
|
||||||
|
config.services.postgresql.port}/lemmy";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.lemmy = {
|
||||||
|
enable = true;
|
||||||
|
ui.port = lemmy-ui.port;
|
||||||
|
database.createLocally = true;
|
||||||
|
settings = {
|
||||||
|
# TODO: Enable this much later when you tested everything.
|
||||||
|
# N.B. you can't change your domain name after enabling this.
|
||||||
|
federation.enabled = false;
|
||||||
|
# settings related to the postgresql database
|
||||||
|
database = {
|
||||||
|
user = "lemmy";
|
||||||
|
password = "secretlemmypassword";
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = ${config.services.postgresql.port};
|
||||||
|
database = "lemmy";
|
||||||
|
pool_size = 5;
|
||||||
|
};
|
||||||
|
# Pictrs image server configuration.
|
||||||
|
pictrs = {
|
||||||
|
# Address where pictrs is available (for image hosting)
|
||||||
|
url = "http://${pict-rs.ip}:${toString pict-rs.port}/";
|
||||||
|
# TODO: Set a custom pictrs API key. ( Required for deleting images )
|
||||||
|
api_key = "";
|
||||||
|
};
|
||||||
|
# TODO: Email sending configuration. All options except login/password are mandatory
|
||||||
|
email = {
|
||||||
|
# Hostname and port of the smtp server
|
||||||
|
smtp_server = "";
|
||||||
|
# Login name for smtp server
|
||||||
|
smtp_login = "";
|
||||||
|
# Password to login to the smtp server
|
||||||
|
smtp_password = "";
|
||||||
|
# Address to send emails from, eg "noreply@your-instance.com";
|
||||||
|
smtp_from_address = "noreply@${lemmy.domain}";
|
||||||
|
# Whether or not smtp connections should use tls. Can be none, tls, or starttls
|
||||||
|
tls_type = "none";
|
||||||
|
};
|
||||||
|
# TODO: Parameters for automatic configuration of new instance (only used at first start)
|
||||||
|
setup = {
|
||||||
|
# Username for the admin user
|
||||||
|
admin_username = "superawesomeadmin";
|
||||||
|
# Password for the admin user. It must be at least 10 characters.
|
||||||
|
admin_password = "";
|
||||||
|
# Name of the site (can be changed later)
|
||||||
|
site_name = "Lemmy at ${lemmy.domain}";
|
||||||
|
# Email for the admin user (optional, can be omitted and set later through the website)
|
||||||
|
admin_email = "admin@${lemmy.domain}";
|
||||||
|
};
|
||||||
|
# the domain name of your instance (mandatory)
|
||||||
|
hostname = lemmy.domain;
|
||||||
|
# Address where lemmy should listen for incoming requests
|
||||||
|
bind = lemmy.ip;
|
||||||
|
# Port where lemmy should listen for incoming requests
|
||||||
|
port = lemmy.port;
|
||||||
|
# Whether the site is available over TLS. Needs to be true for federation to work.
|
||||||
|
tls_enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
# needed for now
|
||||||
|
nixpkgs.config.permittedInsecurePackages = [
|
||||||
|
"nodejs-14.21.3"
|
||||||
|
"openssl-1.1.1t"
|
||||||
|
];
|
||||||
|
|
||||||
|
system.activationScripts."make_sure_lemmy_user_owns_files" = ''
|
||||||
|
uid='${config.users.users.lemmy.uid}';
|
||||||
|
gid='${config.users.groups.lemmy.gid}';
|
||||||
|
dir='${lemmy.dataDir}'
|
||||||
|
|
||||||
|
mkdir -p "''${dir}"
|
||||||
|
|
||||||
|
if [[ "$(${pkgs.toybox}/bin/stat "''${dir}" -c '%u:%g' | tee /dev/stderr )" != "''${uid}:''${gid}" ]]; then
|
||||||
|
chown -R "''${uid}:''${gid}" "''${dir}"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('nextcloud')">
|
||||||
|
services.nextcloud = {
|
||||||
|
enable = true;
|
||||||
|
hostName = "nextcloud.tld";
|
||||||
|
database.createLocally = true;
|
||||||
|
config = {
|
||||||
|
dbtype = "pgsql";
|
||||||
|
adminpassFile = "/path/to/admin-pass-file";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('hedgedoc')">
|
||||||
|
networking.firewall = {
|
||||||
|
allowedTCPPorts = [ 8001 ];
|
||||||
|
};
|
||||||
|
services.hedgedoc = {
|
||||||
|
enable = true;
|
||||||
|
settings.domain = "hedgedoc.nixin.local";
|
||||||
|
settings.port = 8001;
|
||||||
|
settings.host = "0.0.0.0";
|
||||||
|
settings.protocolUseSSL = false;
|
||||||
|
settings.allowOrigin = [
|
||||||
|
"localhost"
|
||||||
|
"hedgedoc.nixin.local"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="nixinServices.includes('forgejoRunner')">
|
||||||
|
virtualisation.containers.enable = true;
|
||||||
|
virtualisation.podman = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Create a `docker` alias for podman, to use it as a drop-in replacement
|
||||||
|
dockerCompat = true;
|
||||||
|
|
||||||
|
# Required for containers under podman-compose to be able to talk to each other.
|
||||||
|
defaultNetwork.settings.dns_enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.gitea-actions-runner = {
|
||||||
|
package = pkgs.forgejo-runner;
|
||||||
|
instances.default = {
|
||||||
|
enable = true;
|
||||||
|
name = "dromadaire";
|
||||||
|
url = "https://git.distrilab.fr";
|
||||||
|
# Obtaining the path to the runner token file may differ
|
||||||
|
tokenFile = "/etc/forgejo/runner.token";
|
||||||
|
labels = [
|
||||||
|
# provide a debian base with nodejs for actions
|
||||||
|
"debian-latest:docker://node:20-bookworm"
|
||||||
|
# fake the ubuntu name, because node provides no ubuntu builds
|
||||||
|
"ubuntu-latest:docker://node:20-bookworm"
|
||||||
|
# nixos
|
||||||
|
"nixos:docker://nixos/nix:latest"
|
||||||
|
# provide native execution on the host
|
||||||
|
#"native:host"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div v-if="nixinServices.includes('forgejo')">
|
||||||
|
services.nginx = {
|
||||||
|
virtualHosts.${cfg.settings.server.DOMAIN} = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
extraConfig = ''
|
||||||
|
client_max_body_size 512M;
|
||||||
|
'';
|
||||||
|
locations."/".proxyPass = "http://localhost:${toString srv.HTTP_PORT}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.forgejo = {
|
||||||
|
enable = true;
|
||||||
|
database.type = "postgres";
|
||||||
|
# Enable support for Git Large File Storage
|
||||||
|
lfs.enable = true;
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
DOMAIN = "git.example.com";
|
||||||
|
# You need to specify this to remove the port from URLs in the web UI.
|
||||||
|
ROOT_URL = "https://${srv.DOMAIN}/";
|
||||||
|
HTTP_PORT = 3000;
|
||||||
|
};
|
||||||
|
# You can temporarily allow registration to create an admin user.
|
||||||
|
service.DISABLE_REGISTRATION = true;
|
||||||
|
# Add support for actions, based on act: https://github.com/nektos/act
|
||||||
|
actions = {
|
||||||
|
ENABLED = true;
|
||||||
|
DEFAULT_ACTIONS_URL = "github";
|
||||||
|
};
|
||||||
|
# Sending emails is completely optional
|
||||||
|
# You can send a test email from the web UI at:
|
||||||
|
# Profile Picture > Site Administration > Configuration > Mailer Configuration
|
||||||
|
mailer = {
|
||||||
|
ENABLED = true;
|
||||||
|
SMTP_ADDR = "mail.example.com";
|
||||||
|
FROM = "noreply@${srv.DOMAIN}";
|
||||||
|
USER = "noreply@${srv.DOMAIN}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mailerPasswordFile = config.age.secrets.forgejo-mailer-password.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
23
installation.md
Normal file
23
installation.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
For now, the NixiN installation process was just tested on a Linux distribution, on a computer with virtualisation capacities.
|
||||||
|
So only requirements are :
|
||||||
|
|
||||||
|
- A Linux system with virtualisation support
|
||||||
|
- With Nix installed <https://nix.dev/manual/nix/stable/installation/installation.html>
|
||||||
|
|
||||||
|
## Grab the code and run locally
|
||||||
|
|
||||||
|
Get the latest version from the official repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.distrilab.fr/NixiN/nixin-web
|
||||||
|
```
|
||||||
|
|
||||||
|
Go in the main folder and run the `shell.nix` to install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd nixin-web && nix-shell
|
||||||
|
```
|
32
inventory/demo-configuration.nix
Normal file
32
inventory/demo-configuration.nix
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vmVariant.virtualisation.forwardPorts = [
|
||||||
|
{ from = "host"; host.port = 8001; guest.port = 8001; }
|
||||||
|
];
|
||||||
|
networking.hosts = {
|
||||||
|
"127.0.0.1" = [ "hedgedoc.nixin.local" ];
|
||||||
|
};
|
||||||
|
networking.hostName = "demo";
|
||||||
|
#networking.firewall.enable = false;
|
||||||
|
networking.firewall = {
|
||||||
|
allowedTCPPorts = [ 8001 ];
|
||||||
|
};
|
||||||
|
services.hedgedoc = {
|
||||||
|
enable = true;
|
||||||
|
settings.domain = "hedgedoc.nixin.local";
|
||||||
|
settings.port = 8001;
|
||||||
|
settings.host = "0.0.0.0";
|
||||||
|
settings.protocolUseSSL = false;
|
||||||
|
settings.allowOrigin = [
|
||||||
|
"localhost"
|
||||||
|
"hedgedoc.nixin.local"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
users.users.operator = {
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
initialPassword = "test";
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = "24.05";
|
||||||
|
}
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,9 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "nixin-web",
|
"name": "NixiN",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"name": "NixiN",
|
||||||
|
"license": "AGPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitepress": "^1.3.3"
|
"vitepress": "^1.3.3"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
{
|
{
|
||||||
|
"name": "NixiN",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://git.distrilab.fr/NixiN/nixin-web.git"
|
||||||
|
},
|
||||||
|
"license": "AGPL-3.0",
|
||||||
|
"homepage": "https://nixin.distrilab.eu",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"docs:dev": "vitepress dev",
|
"docs:dev": "vitepress dev",
|
||||||
"docs:build": "vitepress build",
|
"docs:build": "vitepress build",
|
||||||
|
|
14
shell.nix
Normal file
14
shell.nix
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
with import <nixpkgs> {};
|
||||||
|
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "nixin";
|
||||||
|
buildInputs = [
|
||||||
|
nodejs # for vitepress
|
||||||
|
nixos-generators # for building images
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
export PATH="$PWD/node_modules/.bin/:$PATH"
|
||||||
|
npm install
|
||||||
|
npm run
|
||||||
|
'';
|
||||||
|
}
|
|
@ -1,28 +1,36 @@
|
||||||
# Technical principles
|
# Technical principles
|
||||||
|
|
||||||
|
ToDo: intro on best practices that drive the technical choices
|
||||||
|
|
||||||
## KISS principle
|
## KISS principle
|
||||||
"Keep it simple, stupid!"
|
"Keep it simple, stupid!"
|
||||||
|
|
||||||
## Do not reinvent the wheel
|
## Do not reinvent the wheel
|
||||||
|
ToDo: nixos
|
||||||
|
ToDo: passwordstore
|
||||||
|
ToDo: krops
|
||||||
|
|
||||||
## There is only one timezone
|
## There is only one timezone
|
||||||
Experience has shown that using multiple time-zones for the servers of an infrastructure is a recipe for disaster.
|
Experience has shown that using multiple time-zones for the servers of an infrastructure is a recipe for disaster.
|
||||||
Also, using the timezone of one country for an international project is a source of confusion and headaches.
|
Also, using the timezone of one country for an international project is a source of confusion and headaches.
|
||||||
Especially when that timezone is subject to daylight saving changes that are causing the clock to jump 1 hour forward or backward twice a year.
|
Especially when that timezone is subject to daylight saving changes that are causing the clock to jump 1 hour forward or backward twice a year.
|
||||||
The only sensible choice is to set the servers time to UTC and to transalte the timestamps to the user's timezone when displaying them.
|
The only sensible choice is to set the servers time to UTC and to transalte the timestamps to the user's timezone when displaying them on an interface.
|
||||||
|
|
||||||
This is strongly opinion based. And we may not not all agree on the subject. This is why we will make sure that it is easy for the users to choose their prefered timezone for setting up their servers.
|
This is strongly opinion based. We may not not all agree on the subject. This is why we will make sure that it is easy for the users to choose their prefered timezone for setting up their servers.
|
||||||
|
|
||||||
## Eat your own food
|
## Eat your own dog food
|
||||||
The project is bootstrapped using a hosting infrastructure thagtis based on Proxmox, Debian and YunoHost.
|
The project is bootstrapped using an infrastructure that is based on Proxmox, Debian and YunoHost for hosting its website and git forge.
|
||||||
But the goal is to host the project using itself as soon as possible. That is using NixOS servers managed with the tools and principles developed by the NixiN project.
|
|
||||||
Currently only the forgejo action runners used for CI/CD are hosted on NixOS servers.
|
Currently only the forgejo action runners used for CI/CD are hosted on NixOS servers.
|
||||||
|
But the goal is to host the whole project using itself as soon as possible. That is using NixOS servers managed with the tools and principles developed within the NixiN project.
|
||||||
|
|
||||||
## CI/CD
|
## CI/CD
|
||||||
|
|
||||||
## Focus on user experience
|
## Focus on user experience
|
||||||
|
|
||||||
## Prioritize security
|
## Prioritize security
|
||||||
|
ToDo: only open ports that are strictily necessary on the public interface. go through a VPN for everything else
|
||||||
|
ToDo: use fail2ban or reaction
|
||||||
|
ToDo: passwords manager
|
||||||
|
|
||||||
## No premature performance optimization
|
## No premature performance optimization
|
||||||
Use best practices to write efficient code but do not write overly complicated solutions based on a-priori thinking of performance issue.
|
Use best practices to write efficient code but do not write overly complicated solutions based on a-priori thinking of performance issue.
|
||||||
|
@ -32,3 +40,9 @@ Only optimize what has been tested to be an issue.
|
||||||
Even though we think that Rust would be a better language for developing the tools of the project we are starting the first version using Go because it is faster to develop with it and easier to find contributors with this languages.
|
Even though we think that Rust would be a better language for developing the tools of the project we are starting the first version using Go because it is faster to develop with it and easier to find contributors with this languages.
|
||||||
|
|
||||||
|
|
||||||
|
## ToDo
|
||||||
|
favor modern filesystems with snapshoting capability like zfs and btrfs
|
||||||
|
|
||||||
|
|
||||||
|
## To flake or not to flake?
|
||||||
|
There is a bit of controversy around flakes. They bring some intereting convenience when using NixOS and have spawned an extensive ecosystem. But they are not without drawbacks. We have decided to not use flakes for now. But we'll keep our architecture open for the users who want to use them.
|
||||||
|
|
Loading…
Reference in a new issue