AI prompts
base on NextJS VPS Example # Do we need Vercel? Can we just run our NextJS apps on a VM? Maybe all our apps on one machine?
I love NextJS, but I don't like Vercels pricing. It seems nuts to me and it seems many other people do too. So I spent a few hours playing with this and ~~Digital Ocean~~ Hetzner Cloud (€3.30/mo 🤣) to see what was possible.
All thanks goes to [kamal](https://github.com/basecamp/kamal) and @ImSh4yy, I built this using his [post](https://logsnag.com/blog/self-host-nextjs-hetzner-kamal) 🙏
## What's the objective here?
Figure out if we can have all the requirements of most indie hacker apps on a little VM instead of Vercel?
Here's the list of to dos:
- [x] Can we run NextJS on VPS easily? ✅
- [x] Is the latency acceptable? ✅ 35ms, roughly same as Vercel
- [x] Can we auto deploy? ✅ see [deploy-on-main.yml](.github/workflows/deploy-on-main.yml)
- [x] Can we persist data on this machine when using Docker?
- [x] How much traffic can this machine handle concurrently? Around 750 HTTP requests/sec on Hetzner €3.29/mo VPS, before it starts to slow down, see this load test [report](https://loader.io/reports/e86c09956f73bb12f0e2b15900947a60/results/9ba8eb7e6dc70fd3966f3abed65e2166)
- [x] What's the writes per second using SQL Lite? [✅ 14,000/sec on Hetzner €3.29/mo VPS](https://twitter.com/ashleyrudland/status/1777597718560444498)
- [x] What's the uptime of this? ✅ so far 100%
- [x] NextJS Feature: Image Optimization? ✅ works!
- [x] NextJS Feature: Can we use Server Actions? ✅ SQLite write test runs on Server Actions. See [actions](./src/app/actions/)
- [x] NextJS Feature: API routes? ✅ see [/api/vm/](./src/app/api/vm/)
- [ ] NextJS Feature: Can we use the NextJS Caching? Custom Cache?
- [ ] Can/how we run multiple apps on the same machine? Switch app based on domain name?
## What's not the objective?
- Infinite scale - do indie hackers really need this?
- Complex architecture
### How does this auto deploy?
Basically GitHub actions run on each commit to main, builds image using Docker, uploads then Kamal connects to machine via SSH (with passphrase), then reboots app with new code.
", Assign "at most 3 tags" to the expected json: {"id":"9235","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"