<?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: VeloxaHost</title>
    <description>The latest articles on DEV Community by VeloxaHost (@veloxadev).</description>
    <link>https://dev.to/veloxadev</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4229116%2F67dfa75e-0e23-4209-b42b-c62b672e0090.png</url>
      <title>DEV Community: VeloxaHost</title>
      <link>https://dev.to/veloxadev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/veloxadev"/>
    <language>en</language>
    <item>
      <title>Harden a fresh Ubuntu 24.04 server with one command (open source)</title>
      <dc:creator>VeloxaHost</dc:creator>
      <pubDate>Thu, 05 Nov 2026 18:27:25 +0000</pubDate>
      <link>https://dev.to/veloxadev/harden-a-fresh-ubuntu-2404-server-with-one-command-open-source-2nge</link>
      <guid>https://dev.to/veloxadev/harden-a-fresh-ubuntu-2404-server-with-one-command-open-source-2nge</guid>
      <description>&lt;p&gt;Every new VPS gets the same first hour: create a user, lock down SSH, set up a firewall, install fail2ban, turn on automatic updates… and then forget one of them on the next server.&lt;/p&gt;

&lt;p&gt;So we turned our checklist into a small open-source script: &lt;strong&gt;&lt;a href="https://github.com/veloxahost/server-hardening" rel="noopener noreferrer"&gt;veloxahost/server-hardening&lt;/a&gt;&lt;/strong&gt;. One command, readable configs, safe to re-run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Creates a sudo user and installs your SSH key&lt;/li&gt;
&lt;li&gt;SSH: keys only, no root login, no passwords — via a drop-in file, validated with &lt;code&gt;sshd -t&lt;/code&gt; before restart&lt;/li&gt;
&lt;li&gt;UFW: deny inbound, rate-limit SSH, allow 80/443&lt;/li&gt;
&lt;li&gt;fail2ban with the &lt;strong&gt;systemd backend&lt;/strong&gt; (the reliable choice on Ubuntu 24.04) and escalating bans&lt;/li&gt;
&lt;li&gt;Unattended security upgrades + &lt;code&gt;needrestart&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;auditd rules for identity files, sudoers, sshd config and root commands&lt;/li&gt;
&lt;li&gt;Kernel sysctl hardening that doesn't break Docker&lt;/li&gt;
&lt;li&gt;A Lynis audit at the end so you get a score&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;On a &lt;strong&gt;fresh&lt;/strong&gt; Ubuntu 24.04 server (22.04 and Debian 12 work too), keep your current session open and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/veloxahost/server-hardening.git
&lt;span class="nb"&gt;cd &lt;/span&gt;server-hardening
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;NEW_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;deploy &lt;span class="nv"&gt;SSH_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ssh-ed25519 AAAA... you@example.com"&lt;/span&gt; ./harden.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open a &lt;strong&gt;new&lt;/strong&gt; terminal and check &lt;code&gt;ssh deploy@your-server&lt;/code&gt; works before logging out.&lt;/p&gt;

&lt;p&gt;Options are plain environment variables:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Var&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NEW_USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;sudo user to create&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;required&lt;/td&gt;
&lt;td&gt;public key for that user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSH_PORT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;22&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH port to allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALLOW_PORTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;80,443&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;extra TCP ports to open&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENABLE_IP_FORWARD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;set &lt;code&gt;1&lt;/code&gt; on Docker hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN_LYNIS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;run the audit at the end&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Three things people get wrong on Ubuntu 24.04
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Editing &lt;code&gt;sshd_config&lt;/code&gt; directly.&lt;/strong&gt; 24.04 reads &lt;code&gt;/etc/ssh/sshd_config.d/*.conf&lt;/code&gt; first. Put your settings in a drop-in so package upgrades never overwrite them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. fail2ban watching a log file that's empty.&lt;/strong&gt; SSH logs go to the journal now. Use &lt;code&gt;backend = systemd&lt;/code&gt; or your &lt;code&gt;sshd&lt;/code&gt; jail will show zero failures forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Trusting UFW with Docker.&lt;/strong&gt; Ports published with &lt;code&gt;-p 8080:8080&lt;/code&gt; bypass UFW completely. Bind internal services to &lt;code&gt;127.0.0.1&lt;/code&gt; and put a reverse proxy in front.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not Ansible?
&lt;/h2&gt;

&lt;p&gt;If you already run Ansible, use it. This is for the "one server, five minutes" case: a single bash script you can read top to bottom before you run it as root.&lt;/p&gt;

&lt;h2&gt;
  
  
  More
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full write-up with every command explained: &lt;a href="https://veloxahost.com/blog/posts/linux-server-hardening-checklist.html?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=server-hardening" rel="noopener noreferrer"&gt;The Complete Linux Server Hardening Checklist (2026)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Production Docker Compose stacks with automatic HTTPS: &lt;a href="https://github.com/veloxahost/compose-stacks" rel="noopener noreferrer"&gt;veloxahost/compose-stacks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PRs and issues welcome — especially for other distros.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://veloxahost.com/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=server-hardening" rel="noopener noreferrer"&gt;VeloxaHost&lt;/a&gt;, a hosting platform. The script is MIT-licensed and works on any Linux server.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>devops</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Run Docker inside Incus system containers (profiles + cloud-init)</title>
      <dc:creator>VeloxaHost</dc:creator>
      <pubDate>Thu, 05 Nov 2026 18:25:15 +0000</pubDate>
      <link>https://dev.to/veloxadev/run-docker-inside-incus-system-containers-profiles-cloud-init-51jl</link>
      <guid>https://dev.to/veloxadev/run-docker-inside-incus-system-containers-profiles-cloud-init-51jl</guid>
      <description>&lt;p&gt;If you've only used Docker on a VM, you may not know you can run it inside a &lt;strong&gt;system container&lt;/strong&gt; — no hypervisor, near-native speed, and it boots in a couple of seconds. Incus (the community fork of LXD) makes this easy once you know the three settings that matter.&lt;/p&gt;

&lt;p&gt;All configs below are in &lt;strong&gt;&lt;a href="https://github.com/veloxahost/incus-examples" rel="noopener noreferrer"&gt;veloxahost/incus-examples&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The profile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Docker inside a system container&lt;/span&gt;
&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security.nesting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
  &lt;span class="na"&gt;security.syscalls.intercept.mknod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
  &lt;span class="na"&gt;security.syscalls.intercept.setxattr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
  &lt;span class="na"&gt;linux.kernel_modules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overlay,br_netfilter,ip_tables,ip6_tables,nf_nat,xt_conntrack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;security.nesting&lt;/code&gt; lets the container create its own containers.&lt;/li&gt;
&lt;li&gt;The two syscall intercepts let Docker's overlay filesystem and image layers work in an unprivileged container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;linux.kernel_modules&lt;/code&gt; makes sure the host loads what Docker networking needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Load it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;incus profile create docker-ready
incus profile edit docker-ready &amp;lt; profiles/docker-ready.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Launch with cloud-init
&lt;/h2&gt;

&lt;p&gt;The repo's &lt;a href="https://github.com/veloxahost/incus-examples/blob/main/cloud-init/docker-host.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;cloud-init/docker-host.yaml&lt;/code&gt;&lt;/a&gt; installs Docker Engine and the Compose plugin from Docker's apt repo and sets sane log rotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;incus launch images:ubuntu/24.04/cloud web-01 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; default &lt;span class="nt"&gt;-p&lt;/span&gt; docker-ready &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; cloud-init.user-data&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;cloud-init/docker-host.yaml&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

incus &lt;span class="nb"&gt;exec &lt;/span&gt;web-01 &lt;span class="nt"&gt;--&lt;/span&gt; cloud-init status &lt;span class="nt"&gt;--wait&lt;/span&gt;
incus &lt;span class="nb"&gt;exec &lt;/span&gt;web-01 &lt;span class="nt"&gt;--&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add limits
&lt;/h2&gt;

&lt;p&gt;Don't let one container eat the host:&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;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;limits.cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
  &lt;span class="na"&gt;limits.memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1GiB&lt;/span&gt;
  &lt;span class="na"&gt;limits.memory.enforce&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hard&lt;/span&gt;
  &lt;span class="na"&gt;limits.processes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Snapshots before every upgrade
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/snapshot-rotate.sh web-01 7   &lt;span class="c"&gt;# take one, keep the newest 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it from cron and &lt;code&gt;docker compose pull &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt; stops being scary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container or VM?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;System container&lt;/th&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Boot&lt;/td&gt;
&lt;td&gt;~1–3 s&lt;/td&gt;
&lt;td&gt;~10–30 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;near zero&lt;/td&gt;
&lt;td&gt;full kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker inside&lt;/td&gt;
&lt;td&gt;yes (nesting)&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom kernel modules&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For web apps, databases and Compose stacks, a container is usually the better trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't want to run the host?
&lt;/h2&gt;

&lt;p&gt;That's essentially what a &lt;a href="https://veloxahost.com/vps?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=incus-examples" rel="noopener noreferrer"&gt;VeloxaHost&lt;/a&gt; server is: a root system container that runs Docker, from $4/mo with snapshots and an API.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: we run VeloxaHost. The repo is MIT and works on any Incus host.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>devops</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>30 AI website builder prompts that produce usable sites (free, CC0)</title>
      <dc:creator>VeloxaHost</dc:creator>
      <pubDate>Thu, 05 Nov 2026 18:19:41 +0000</pubDate>
      <link>https://dev.to/veloxadev/30-ai-website-builder-prompts-that-produce-usable-sites-free-cc0-18ej</link>
      <guid>https://dev.to/veloxadev/30-ai-website-builder-prompts-that-produce-usable-sites-free-cc0-18ej</guid>
      <description>&lt;p&gt;Most "build me a website" prompts produce the same generic page: a hero, three feature cards, lorem ipsum. The fix isn't a longer prompt — it's a more &lt;em&gt;specific&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;We put together a library of specific, structured prompts built on six simple rules: &lt;strong&gt;&lt;a href="https://github.com/veloxahost/veloxa-build-prompts" rel="noopener noreferrer"&gt;veloxahost/veloxa-build-prompts&lt;/a&gt;&lt;/strong&gt; — 30 prompts, CC0, works in Lovable, Bolt, v0, Replit or Veloxa Build.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 rules behind every prompt
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name the business.&lt;/strong&gt; "Rise &amp;amp; Crumb bakery" gets you real copy; "a bakery" gets you filler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;List sections in order.&lt;/strong&gt; The AI follows your structure instead of inventing one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spell out every form field.&lt;/strong&gt; "A pre-order form (name, phone, pickup date, items)" beats "a contact form".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Give it a look.&lt;/strong&gt; Two or three colours, a mood word, and "mobile first".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Say where data lives.&lt;/strong&gt; For mini-apps, "save in the browser" avoids half-built backends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate one change at a time.&lt;/strong&gt; Small follow-ups beat one 2,000-word prompt.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local business (plumber):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a website for a local plumbing company "FlowRight Plumbing". Hero with a big 'Call now' button and emergency 24/7 badge, services grid (leaks, water heaters, drains, remodels), service-area list of towns, a quote request form with photo upload, trust badges section (licensed, insured, years in business) and a reviews section with placeholder text.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SaaS waitlist:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a landing page for a SaaS product "ShipNote" that turns git commits into release notes. Sections: hero with a headline, subheadline and email waitlist form, a 3-feature grid with icons, a 'how it works' diagram in 3 steps, pricing teaser (Free, Pro), FAQ and footer. Show a success message after signup. Modern, purple accent.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Working mini-app (expense splitter):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build an app to split group expenses. Create a group, add people, add expenses (who paid, amount, who shares it, equal or custom split), and show balances plus the minimum set of payments to settle up. Mobile first, save in the browser, export a summary as text.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's in the repo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Small-business sites: bakery, restaurant, salon, contractor, accountant, real estate, trainer, therapist&lt;/li&gt;
&lt;li&gt;Personal: designer and developer portfolios, link-in-bio, résumé, wedding RSVP&lt;/li&gt;
&lt;li&gt;Startup/SaaS: waitlist, product launch, pricing page, changelog, status page&lt;/li&gt;
&lt;li&gt;Apps: habit tracker, invoice generator, to-do, expense splitter, RSVP manager, flashcards, booking page, memory game&lt;/li&gt;
&lt;li&gt;Community: nonprofit, club, tutor, band&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every prompt has a one-click button that drops it into the builder's message box without sending, so you can tweak it first.&lt;/p&gt;

&lt;p&gt;Got a prompt that works well? &lt;a href="https://github.com/veloxahost/veloxa-build-prompts" rel="noopener noreferrer"&gt;Open a PR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: we build &lt;a href="https://veloxahost.com/veloxa-build?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=prompts" rel="noopener noreferrer"&gt;Veloxa Build&lt;/a&gt;. The prompts are CC0 and builder-agnostic.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>nocode</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to host a Lovable, Bolt or v0 app on your own domain (configs included)</title>
      <dc:creator>VeloxaHost</dc:creator>
      <pubDate>Thu, 05 Nov 2026 18:18:54 +0000</pubDate>
      <link>https://dev.to/veloxadev/how-to-host-a-lovable-bolt-or-v0-app-on-your-own-domain-configs-included-14jk</link>
      <guid>https://dev.to/veloxadev/how-to-host-a-lovable-bolt-or-v0-app-on-your-own-domain-configs-included-14jk</guid>
      <description>&lt;p&gt;You built an app in Lovable, Bolt or v0, clicked export, and now you have a zip or a GitHub repo. Getting it onto &lt;strong&gt;your own domain&lt;/strong&gt; is where most people get stuck — blank pages, routes that 404 on refresh, env vars that vanish.&lt;/p&gt;

&lt;p&gt;We collected the configs we use into an open-source repo: &lt;strong&gt;&lt;a href="https://github.com/veloxahost/host-ai-built-apps" rel="noopener noreferrer"&gt;veloxahost/host-ai-built-apps&lt;/a&gt;&lt;/strong&gt;. Here's the short version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Work out what you actually exported
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You have…&lt;/th&gt;
&lt;th&gt;It is&lt;/th&gt;
&lt;th&gt;Serve it with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;index.html&lt;/code&gt; + CSS/JS, no &lt;code&gt;package.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Plain static site&lt;/td&gt;
&lt;td&gt;Any static file server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;package.json&lt;/code&gt; with &lt;code&gt;vite&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Vite single-page app (typical Lovable/Bolt)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;npm run build&lt;/code&gt;, then serve &lt;code&gt;dist/&lt;/code&gt; with an SPA fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;package.json&lt;/code&gt; with &lt;code&gt;next&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Next.js (typical v0)&lt;/td&gt;
&lt;td&gt;Node server or Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Backend check:&lt;/strong&gt; if the app uses Supabase (Lovable) or the builder's own auth/database, that part keeps talking to those services. Add your new domain to their allowed origins and redirect URLs before you switch DNS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Serve a Vite app with automatic HTTPS
&lt;/h2&gt;

&lt;p&gt;On any Ubuntu server with your domain's A record pointing at it:&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; caddy
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/www/app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; dist/&lt;span class="k"&gt;*&lt;/span&gt; /var/www/app/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="n"&gt;encode&lt;/span&gt; &lt;span class="n"&gt;zstd&lt;/span&gt; &lt;span class="n"&gt;gzip&lt;/span&gt;
    &lt;span class="n"&gt;try_files&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
    &lt;span class="n"&gt;file_server&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/*&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="s"&gt;"public, max-age=31536000, immutable"&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="s"&gt;"no-cache"&lt;/span&gt;
&lt;span class="p"&gt;}&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload caddy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caddy fetches a Let's Encrypt certificate on the first request. The &lt;code&gt;try_files … /index.html&lt;/code&gt; line is the important one: it's what stops &lt;code&gt;/dashboard&lt;/code&gt; from 404-ing when someone refreshes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Next.js exports
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;output: "standalone"&lt;/code&gt; in &lt;code&gt;next.config.js&lt;/code&gt;, then use the multi-stage &lt;a href="https://github.com/veloxahost/host-ai-built-apps/blob/main/docker/Dockerfile.next" rel="noopener noreferrer"&gt;Dockerfile in the repo&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; myapp &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:3000:3000 &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and reverse-proxy to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;reverse_proxy&lt;/span&gt; &lt;span class="m"&gt;127.0.0.1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Deploy on every push
&lt;/h2&gt;

&lt;p&gt;The repo includes a &lt;a href="https://github.com/veloxahost/host-ai-built-apps/blob/main/github-actions/deploy-static.yml" rel="noopener noreferrer"&gt;GitHub Actions workflow&lt;/a&gt; that builds the app (if there's a &lt;code&gt;package.json&lt;/code&gt;) and &lt;code&gt;rsync&lt;/code&gt;s it to your server. Add &lt;code&gt;DEPLOY_HOST&lt;/code&gt;, &lt;code&gt;DEPLOY_USER&lt;/code&gt; and &lt;code&gt;DEPLOY_SSH_KEY&lt;/code&gt; as secrets and you're done.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five problems you'll probably hit
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Blank page&lt;/strong&gt; → the build used a sub-path. Set &lt;code&gt;base: '/'&lt;/code&gt; in &lt;code&gt;vite.config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;404 on refresh&lt;/strong&gt; → missing SPA fallback (see Step 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Env vars missing&lt;/strong&gt; → Vite only exposes &lt;code&gt;VITE_*&lt;/code&gt; vars, baked in at build time. Rebuild after changing them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login redirect fails&lt;/strong&gt; → add the new domain to your auth provider's redirect URLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mixed content&lt;/strong&gt; → every API URL must be &lt;code&gt;https://&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where to run it
&lt;/h2&gt;

&lt;p&gt;Any Linux VPS works — the configs aren't tied to a provider. If you want the shortcut: &lt;a href="https://veloxahost.com/vps?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=host-ai-built-apps" rel="noopener noreferrer"&gt;VeloxaHost&lt;/a&gt; servers start at $4/mo with snapshots, and &lt;a href="https://veloxahost.com/veloxa-build?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=host-ai-built-apps" rel="noopener noreferrer"&gt;Veloxa Build&lt;/a&gt; hosts what it builds on every plan.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: we build VeloxaHost and Veloxa Build. The repo is MIT-licensed and provider-neutral — PRs welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
