<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexey</title>
    <description>The latest articles on DEV Community by Alexey (@anxi0uz).</description>
    <link>https://dev.to/anxi0uz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3931418%2Fc542fda7-5e8e-4015-9f7c-16728c480054.jpg</url>
      <title>DEV Community: Alexey</title>
      <link>https://dev.to/anxi0uz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anxi0uz"/>
    <language>en</language>
    <item>
      <title>My friend wanted GitLab. He got Gitea and Nextcloud for Obsidian instead.</title>
      <dc:creator>Alexey</dc:creator>
      <pubDate>Thu, 14 May 2026 14:22:41 +0000</pubDate>
      <link>https://dev.to/anxi0uz/my-friend-wanted-gitlab-he-got-gitea-and-nextcloud-for-obsidian-instead-500i</link>
      <guid>https://dev.to/anxi0uz/my-friend-wanted-gitlab-he-got-gitea-and-nextcloud-for-obsidian-instead-500i</guid>
      <description>&lt;p&gt;A friend sent me an article about GitHub potentially getting blocked in Russia and asked me to spin up GitLab. I suggested Gitea — I'd used it at a college hackathon, knew it was lightweight and wouldn't eat half the server. He agreed.&lt;/p&gt;

&lt;p&gt;While the deploy was running, I asked him how he syncs Obsidian. He said — plain WebDAV, nothing fancy. Well, server's already open anyway, so I threw in Nextcloud too. Hour and a half later I had both a git host and a cloud storage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not GitLab
&lt;/h2&gt;

&lt;p&gt;My friend originally wanted GitLab. I opened the docs, looked at the requirements — 4 GB RAM just to start — and said no. We don't have a dedicated git server, there's already a project running on it. GitLab would've eaten everything.&lt;/p&gt;

&lt;p&gt;Gitea idles at ~150 MB. Actions are compatible with GitHub Actions syntax, so existing workflows move over without rewriting. I'd already used it at a hackathon, knew it worked fine. Suggested it, got the green light.&lt;/p&gt;




&lt;h2&gt;
  
  
  On Helm
&lt;/h2&gt;

&lt;p&gt;First time I touched Kubernetes was when I had to deploy a college project for a grade. Wrote manifests by hand — Deployment, Service, Ingress, PVC, repeat. I knew Helm existed but never had a reason to dig into it.&lt;/p&gt;

&lt;p&gt;Turns out it's like pacman, but for Kubernetes. One &lt;code&gt;values.yaml&lt;/code&gt; instead of five hundred lines of YAML, one command — everything's up. Would've lost my mind doing it the old way.&lt;/p&gt;

&lt;p&gt;First though, Helm couldn't see the cluster at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kubernetes cluster unreachable: Get "http://localhost:8080/version"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;k3s puts the kubeconfig somewhere Helm doesn't look by default. Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KUBECONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/rancher/k3s/k3s.yaml
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Gitea
&lt;/h2&gt;

&lt;p&gt;The server already had ingress-nginx and cert-manager with a &lt;code&gt;letsencrypt-prod&lt;/code&gt; ClusterIssuer. Set the DNS beforehand — A record &lt;code&gt;git.logiflowadvanced.online → 2.27.42.100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gitea-values.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git.logiflowadvanced.online&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitea-tls&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;git.logiflowadvanced.online&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cert-manager.io/cluster-issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-prod&lt;/span&gt;

&lt;span class="na"&gt;gitea&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yourpassword&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your@email.com&lt;/span&gt;

&lt;span class="na"&gt;persistence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;

&lt;span class="na"&gt;postgresql-ha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add gitea-charts https://dl.gitea.com/charts/
helm repo update
helm &lt;span class="nb"&gt;install &lt;/span&gt;gitea gitea-charts/gitea &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; gitea &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; gitea-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pods came up. Opened the browser — invalid certificate, browser complaining. Checked the Ingress:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME    CLASS    HOSTS                          ADDRESS   PORTS
&lt;/span&gt;&lt;span class="gp"&gt;gitea   &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;git.logiflowadvanced.online              80, 443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CLASS: &amp;lt;none&amp;gt;&lt;/code&gt; — the nginx controller just ignored this Ingress entirely. cert-manager didn't issue anything either, so nginx was serving its default self-signed cert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch ingress gitea &lt;span class="nt"&gt;-n&lt;/span&gt; gitea &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'[{"op":"add","path":"/spec/ingressClassName","value":"nginx"}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the patch, cert-manager issued the certificate and the site opened fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;gitea-ssh&lt;/code&gt; is created as a headless ClusterIP by default — not reachable from outside. Port 22 is taken by the system SSH, so I needed a NodePort. You can't patch a headless service into NodePort — have to delete and recreate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete svc gitea-ssh &lt;span class="nt"&gt;-n&lt;/span&gt; gitea

kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Service
metadata:
  name: gitea-ssh
  namespace: gitea
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: gitea
  ports:
    - port: 22
      targetPort: 2222
      nodePort: 30022
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remote looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add gitea ssh://git@git.logiflowadvanced.online:30022/username/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Nextcloud
&lt;/h2&gt;

&lt;p&gt;Set up the DNS for &lt;code&gt;cloud.logiflowadvanced.online&lt;/code&gt; while Gitea was still deploying.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nextcloud-values.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud.logiflowadvanced.online&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextcloud-tls&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloud.logiflowadvanced.online&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cert-manager.io/cluster-issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;letsencrypt-prod&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/proxy-body-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0"&lt;/span&gt;

&lt;span class="na"&gt;nextcloud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud.logiflowadvanced.online&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yourpassword&lt;/span&gt;

&lt;span class="na"&gt;mariadb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yourdbpassword&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextcloud&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nextcloud&lt;/span&gt;

&lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;persistence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;nextcloud nextcloud/nextcloud &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; nextcloud &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; nextcloud-values.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same &lt;code&gt;ingressClassName&lt;/code&gt; issue — same patch, same result.&lt;/p&gt;

&lt;p&gt;After the first login it kept redirecting to &lt;code&gt;/login/cleary?=1&lt;/code&gt;. Nextcloud didn't know its own external address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; nextcloud deploy/nextcloud &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  php occ config:system:set overwriteprotocol &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https"&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; nextcloud deploy/nextcloud &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  php occ config:system:set overwrite.cli.url &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://cloud.logiflowadvanced.online"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Obsidian
&lt;/h3&gt;

&lt;p&gt;Created separate users for me and my friend. WebDAV URL per user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://cloud.logiflowadvanced.online/remote.php/dav/files/username/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the RemotelySave plugin: type — WebDAV, URL, login, password. Works.&lt;/p&gt;




&lt;p&gt;The thing that ate most of my time wasn't Gitea or Nextcloud — it was &lt;code&gt;ingressClassName&lt;/code&gt;. Neither chart sets it automatically, and without it nginx just ignores the Ingress completely. cert-manager doesn't issue anything. Browser shows self-signed, you stare at the logs, pods are all Running, no errors anywhere.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;kubectl get ingress -n &amp;lt;namespace&amp;gt;&lt;/code&gt; right after deploy. If CLASS says &lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt; — that's your problem.&lt;/p&gt;

&lt;p&gt;The other non-obvious one: &lt;code&gt;gitea-ssh&lt;/code&gt; is headless and you can't patch it into a NodePort — you have to delete and recreate it. Spent a few minutes trying to patch it before actually reading the error.&lt;/p&gt;

&lt;p&gt;With Nextcloud and MariaDB — if MariaDB didn't come up on the first deploy or you uninstalled and reinstalled, helm will complain about credential mismatch on upgrade. Just &lt;code&gt;helm uninstall&lt;/code&gt; + &lt;code&gt;kubectl delete pvc --all -n nextcloud&lt;/code&gt; and start fresh, it's faster than untangling the creds.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>git</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
