{"id":20318164,"url":"https://github.com/deadcoder0904/easypanel-nextjs-sqlite","last_synced_at":"2025-08-02T20:34:38.003Z","repository":{"id":221793263,"uuid":"755219303","full_name":"deadcoder0904/easypanel-nextjs-sqlite","owner":"deadcoder0904","description":"Next.js + Drizzle + SQLite + Docker + Litestream (Database Backups) on Easypanel","archived":false,"fork":false,"pushed_at":"2024-04-01T11:21:01.000Z","size":136,"stargazers_count":22,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-11T18:04:16.106Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deadcoder0904.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-09T17:12:43.000Z","updated_at":"2025-04-10T16:39:42.000Z","dependencies_parsed_at":"2024-03-01T14:30:06.184Z","dependency_job_id":"6076b963-2390-4186-a9e2-e5687a8f512d","html_url":"https://github.com/deadcoder0904/easypanel-nextjs-sqlite","commit_stats":null,"previous_names":["deadcoder0904/easypanel-nextjs-sqlite"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/deadcoder0904/easypanel-nextjs-sqlite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deadcoder0904%2Feasypanel-nextjs-sqlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deadcoder0904%2Feasypanel-nextjs-sqlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deadcoder0904%2Feasypanel-nextjs-sqlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deadcoder0904%2Feasypanel-nextjs-sqlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deadcoder0904","download_url":"https://codeload.github.com/deadcoder0904/easypanel-nextjs-sqlite/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deadcoder0904%2Feasypanel-nextjs-sqlite/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268448362,"owners_count":24252019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T18:38:24.363Z","updated_at":"2025-08-02T20:34:37.948Z","avatar_url":"https://github.com/deadcoder0904.png","language":"TypeScript","readme":"### easypanel-nextjs-sqlite\n\n\u003e Trying to get Next.js + SQLite combo working on [Easypanel](https://easypanel.io) with Docker\n\nIn real app, do not commit `.env.development` \u0026 `.env.production` to source control like Git. Add it to `.gitignore` \u0026 `.dockerignore` or better yet use [dotenvx](https://dotenvx.com) for environment variables.\n\nCreate `.env.development` \u0026 `.env.production` using `.env.example` format.\n\n\u003e NOTE: `SQLITE_DATABASE_NAME` should be equal to `users.${MODE}.sqlite` where `MODE` is an environment variable in `.env.*` as it is referenced in `run.sh`. For example, don't use `SQLITE_DATABASE_NAME=users.dev.sqlite` in `.env.development` if you are using `MODE=development` in the same file. You have to either use `MODE=dev` or `SQLITE_DATABASE_NAME=users.development.sqlite`. I like using long-form.\n\nNPM Scripts appended with `:prod` are production scripts and those without anything appended are scripts to be used in development.\n\nUse `/data` in `.env.production` like `SQLITE_DATABASE_PATH=/data/users.production.sqlite` \u0026 setup Cloudflare environment variables to have Database Backups using Litestream.\n\n### TODOS\n\n- [ ] Get `development/Dockerfile` to support HMR (Currently, Dockerfile in development does not work but `pnpm dev` is much better anyways)\n- [ ] Switch back to using `docker-compose.yml` for `development` \u0026 `staging` as docker compose doesn't work only in production on a VPS like Easypanel. Most of the changes were in Dockerfile \u0026 Easypanel Settings so all the complexity isn't needed at all.\n\n\u003e Note: If you need Redis, then only setup Redis in Docker \u0026 use local development environment for HMR as setting docker in development is very tedious \u0026 useless (at least it was in my case.)\n\n### Development Side\n\n1. `pnpm db:generate` generates migration files for `development` at `/src/app/db/migrations`\n2. `pnpm db:migrate` generates `users.dev.sqlite` for `development`\n3. `pnpm turbo` or `pnpm dev` runs the local server.\n4. if you try to interact with database by clicking add, delete, or get buttons, then it creates `*.sqlite-shm` or `*.sqlite-wal` files. read more about [wal mode](https://www.sqlite.org/wal.html) at https://til.simonwillison.net/sqlite/enabling-wal-mode.\n\n### Production Side\n\n1. `make build-production` to build a Docker Container for `production`\n2. `make start-production` to start the Docker Container\n3. `make stop-production` to stop the Docker Container\n4. `docker system prune -f \u0026\u0026 docker builder prune -f` to delete all images \u0026 container\n\n### Hosting on Easypanel\n\n1. Go to `Environment` \u0026 paste `.env.production` into `Environment Variables` \u0026 check `Create .env file` to create `.env` file.\n2. Change port to `3001` as specified in `Dockerfile`. Go into `Domains`, click on `Edit` button, change `Internal Port` to `3001`. Make sure to use Custom Domain as Easypanel currently isn't working on `*.easypanel.host` domains.\n3. Go to `Source`, add `Github` credentials, choose `Dockerfile` \u0026 paste `docker/production/Dockerfile` as the location.\n4. Enable `Auto Deploy` by checking the box besides `Destroy` (delete icon) button.\n5. Finally, click on `Deploy` to launch it.\n\n### SQLite WAL Mode Caveats\n\nI noticed SQLite WAL Mode on Docker Container doesn't work too well \u0026 results in data loss when the `*.sqlite` file is opened in a database browser like `SQLite Database` Desktop App.\n\nReproduction steps (you have to remove litestream specific code as WAL mode works with Litestream... see below) to see this issue after enabling WAL mode in 2 places (search `journal_mode=WAL` in VSCode):\n\n1. Click `Add` in `localhost:3000`\n2. Click `Get All`\n3. Open the Desktop App `SQLite Database` by installing it from https://sqlitebrowser.org/\n4. Click `Add` again multiple times \u0026 try to refresh database inside `SQLite Database` Desktop App\n5. Notice, how the data doesn't update in the Desktop app but works fine in `localhost:3000`\n6. Now close the Docker Container resulting in a data loss\n\nFor this reason, I'll be avoiding WAL mode for now. When the time comes \u0026 I need multiple writes, I'll use PostgreSQL instead of SQLite if I need multiple writers on a database but since the process of multiple writes is instantanious (milliseconds) so I'll be going with SQLite for now anyways.\n\n### SQLite WAL Mode now works when used in combination with Litestream\n\nI repeated the above 6 steps exactly as specified \u0026 there was no data loss.\n\nI guess Litestream wrote it to its WAL Mode \u0026 when it found a wrong pointer, Litestream restored the database.\n\nThis was the log from Litestream that got me to this conclusion:\n\n```bash\ntime=2024-02-28T05:44:50.247Z level=WARN msg=\"init: cannot determine last wal position, clearing generation\" db=/data/users.prod.sqlite error=\"primary wal header: EOF\"\ntime=2024-02-28T05:44:50.406Z level=INFO msg=\"sync: new generation\" db=/data/users.prod.sqlite generation=ab8dd20a19bb28f7 reason=\"no generation exists\"\ntime=2024-02-28T05:44:51.298Z level=INFO msg=\"write snapshot\" db=/data/users.prod.sqlite replica=s3 position=ab8dd20a19bb28f7/00000000:4152\ntime=2024-02-28T05:44:51.720Z level=INFO msg=\"snapshot written\" db=/data/users.prod.sqlite replica=s3 position=ab8dd20a19bb28f7/00000000:4152 elapsed=422.755427ms sz=1512\ntime=2024-02-28T05:44:52.234Z level=INFO msg=\"write wal segment\" db=/data/users.prod.sqlite replica=s3 position=ab8dd20a19bb28f7/00000000:0\ntime=2024-02-28T05:44:52.602Z level=INFO msg=\"wal segment written\" db=/data/users.prod.sqlite replica=s3 position=ab8dd20a19bb28f7/00000000:0 elapsed=367.834931ms sz=4152\n```\n\n#### Easypanel Volume Mount\n\n1. Go to `Storage` \u003e Click `Add Volume Mount` \u003e Put `Name` as anything and `Mount Path` as `/etc/easypanel/projects/[project]/[services]/volumes/data/`\n2. Use `/data` as directory\n\nHypothesis:\n\n1. Use `/etc/easypanel/projects/[project]/[services]/volumes/data/` as `Data Path`\n2. Use `/data` as directory\n\n#### Easypanel Port fix\n\nI used `Domains \u003e Port` \u0026 added `3001` as internal port on my custom domain \u0026 it worked. It didn't work on `*.easypanel.host` domain for some reason.\n\nI had `Mounts \u003e Add Volume Mount` set to `data` as `Name` \u0026 `/data` as `Mount Path` which I don't think is needed if I use `VOLUMES [\"/data\"]` in `Dockerfile`.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeadcoder0904%2Feasypanel-nextjs-sqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeadcoder0904%2Feasypanel-nextjs-sqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeadcoder0904%2Feasypanel-nextjs-sqlite/lists"}