Skip to content

Instantly share code, notes, and snippets.

@RajaniCode
Last active March 22, 2025 09:42
Show Gist options
  • Save RajaniCode/acd33ce0bb7f3ede25d110d51aed0fc2 to your computer and use it in GitHub Desktop.
Save RajaniCode/acd33ce0bb7f3ede25d110d51aed0fc2 to your computer and use it in GitHub Desktop.
Podman Node.js MongoDB MinIO
###########################################################################################################################
# Podman # Node.js # MongoDB # MinIO
###########################################################################################################################
***************************************************************************************************************************
# Node.js # node-mongodb-app:version1.0.0
***************************************************************************************************************************
===========================================================================================================================
node-mongodb-app
├── index.js
├── public
│   ├── images
│   │   ├── MongoDB.png
│   │   └── Node.png
│   └── tachyons.min.css
└── views
└── index.pug
===========================================================================================================================
% nano index.js
===========================================================================================================================
const path = require('path')
const express = require('express')
const { MongoClient } = require('mongodb')
const multer = require('multer')
const { marked } = require('marked')
const app = express()
const port = process.env.PORT || 3000
const mongoURL = process.env.MONGO_URL || 'mongodb://localhost:27017/dev'
const client = new MongoClient(mongoURL)
async function initMongo() {
console.log('Initialising MongoDB...')
console.log(`MongoDB URL: ${mongoURL}`)
let success = false
while (!success) {
try {
await client.connect()
success = true
} catch {
console.log('Error connecting to MongoDB, retrying in 1 second')
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
console.log('MongoDB initialised')
return client.db(client.s.options.dbName).collection('notes')
}
async function start() {
const db = await initMongo()
app.set('view engine', 'pug')
app.set('views', path.join(__dirname, 'views'))
app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, '/images')))
app.get('/', async (req, res) => {
res.render('index', { notes: await retrieveNotes(db) })
})
app.post(
'/note',
multer({ dest: path.join(__dirname, 'public/uploads/') }).single('image'),
async (req, res) => {
if (!req.body.upload && req.body.description) {
await saveNote(db, { description: req.body.description })
res.redirect('/')
} else if (req.body.upload && req.file) {
const link = `/uploads/${encodeURIComponent(req.file.filename)}`
res.render('index', {
content: `${req.body.description} ![](${link})`,
notes: await retrieveNotes(db),
})
}
},
)
app.listen(port, () => {
console.log(`App listening on http://localhost:${port}`)
})
}
async function saveNote(db, note) {
await db.insertOne(note)
}
async function retrieveNotes(db) {
const notes = await db.find().toArray()
const sortedNotes = notes.reverse()
return sortedNotes.map(it => ({ ...it, description: marked(it.description) }))
}
start()
===========================================================================================================================
% nano views/index.pug
===========================================================================================================================
html
head
title= title
link(rel="stylesheet", href="tachyons.min.css")
body.ph3.pt0.pb4.mw7.center.sans-serif
h1.f2.mb0 #[img(src='/images/Node.png')] #[span.green N]ode.js #[img(src='/images/MongoDB.png')] #[span.green M]ongoDB
<!-- img(src="https://avatars.githubusercontent.com/u/9950313?s=48&v=4") -->
// img(src='https://avatars.githubusercontent.com/u/45120?s=48&v=4')
p.f5.mt1.mb4.lh-copy A Node.js-MongoDB app.
form(action="/note" method="POST" enctype="multipart/form-data")
ol.list.pl0
li.mv3
label.f6.b.db.mb2(for="image") Upload an image
input.f6.link.dim.br1.ba.b--black-20.ph3.pv2.mb2.dib.black.bg-white.pointer(type="file" name="image")
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer.ml2(type="submit" value="Upload" name="upload")
li.mv3
label.f6.b.db.mb2(for="description") Write your content here
textarea.f4.db.border-box.hover-black.w-100.measure.ba.b--black-20.pa2.br2.mb2(rows="5" name="description") #{content || ''}
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer(type="submit" value="Publish" name="publish")
if notes.length > 0
ul.list.pl0
p.f6.b.db.mb2 Notes
each note in notes
li.mv3.bb.bw2.b--light-blue.bg-washed-blue.ph4.pv2
p.measure!= note.description
else
p.lh-copy.f6 No notes!
===========================================================================================================================
% curl -L https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css
% wget https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css --directory-prefix=public
% cat public/tachyons.min.css
% curl --output Node.png "https://avatars.githubusercontent.com/u/9950313?s=48&v=4" --create-dirs --output-dir public/images
% ls public/images/Node.png
% curl --output MongoDB.png "https://avatars.githubusercontent.com/u/45120?s=48&v=4" --create-dirs --output-dir public/images
% ls public/images/MongoDB.png
===========================================================================================================================
[
% ls node-mongodb-app/public/uploads
% rm -rf node-mongodb-app/public/uploads
]
% brew services start mongodb-community
% brew services stop mongodb-community
% brew services restart mongodb-community
% mongosh
> db.version()
> show dbs
> use dev
> show collections
[
> db.notes.find()
]
> db.dropDatabase()
> use test
> show dbs
> exit
% node --version
% npm --version
% npm list --global
% npm update --global
% npm list --global
% npm init
[
# package name:
node-mongodb-app-macos-arm64-v1
# description
A Node.js-MongoDB app.
# keywords
Node.js, MongoDB
# author:
RajaniCode
# Is this OK? (yes)
yes
]
% npm install express
% npm install mongodb
% npm install marked
% npm install multer
% npm install pug
% npm list
% npm update
% npm list
% node index.js
<cotrol + C>
% nano Dockerfile
[
FROM node:23.6.1-slim
COPY . .
RUN npm install
CMD [ "node", "index.js" ]
]
% cat Dockerfile
% echo node_modules > .dockerignore
% cat .dockerignore
% podman build -t node-mongodb-app-macos-arm64-v1 .
% podman container list
% podman image list --all
% podman tag localhost/node-mongodb-app-macos-arm64-v1 ghcr.io/rajanicode/node-mongodb-app:version1.0.0
% podman image list --all
# https://github.com/settings/tokens
# Token Till Focus
% export CR_PAT=****************************************
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin
[
Login Succeeded!
]
% podman push ghcr.io/rajanicode/node-mongodb-app:version1.0.0
[
Writing manifest to image destination
]
https://github.com/users/RajaniCode/packages/container/package/node-mongodb-app
===========================================================================================================================
***************************************************************************************************************************
# Node.js # node-mongodb-app:version2.0.0
***************************************************************************************************************************
node-mongodb-app
├── index.js
├── public
│   ├── custom.style.css
│   ├── images
│   │   ├── Minio.png
│   │   ├── MongoDB.png
│   │   └── Node.png
│   └── tachyons.min.css
└── views
└── index.pug
===========================================================================================================================
% nano index.js
===========================================================================================================================
const path = require('path')
const express = require('express')
const { MongoClient } = require('mongodb')
const multer = require('multer')
const { marked } = require('marked')
// minio
const minio = require('minio')
const app = express()
const port = process.env.PORT || 3000
const mongoURL = process.env.MONGO_URL || 'mongodb://localhost:27017/dev'
const client = new MongoClient(mongoURL)
// minio
const minioHost = process.env.MINIO_HOST || 'localhost'
// Destination bucket name
const minioBucket = minioHost + '-bucket'
const minioPort = process.env.MINIO_PORT || 9000
const minioAccessKey = process.env.MINIO_ACCESS_KEY || 'minioadmin'
const minioSecretKey = process.env.MINIO_SECRET_KEY || 'minioadmin'
const useTLS = process.env.MINIO_TLS || 'false'
const useSSL = useTLS.toLowerCase() == 'true' ? true : false
async function initMongo() {
console.log('Initialising MongoDB...')
console.log(`MongoDB URL: ${mongoURL}`)
let success = false
while (!success) {
try {
await client.connect()
success = true
} catch {
console.log('Error connecting to MongoDB, retrying in 1 second')
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
console.log('MongoDB initialised')
return client.db(client.s.options.dbName).collection('notes')
}
// minio
async function initMinIO() {
console.log('Initialising MinIO...')
console.log(`Minio Bucket: ${minioBucket}`)
console.log(`Minio Host: ${minioHost}`)
console.log(`Minio Port: ${minioPort}`)
console.log(`Minio TLS: ${useSSL}`)
console.log(`Minio Access Key: ${minioAccessKey}`)
console.log(`Minio Secret Key: ${minioSecretKey}`)
const client = new minio.Client({
endPoint: minioHost,
port: 9000,
useSSL: false,
accessKey: process.env.MINIO_ACCESS_KEY,
secretKey: process.env.MINIO_SECRET_KEY,
})
let success = false
while (!success) {
try {
if (!(await client.bucketExists(minioBucket))) {
await client.makeBucket(minioBucket)
}
success = true
} catch {
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
console.log('MinIO initialised')
return client
}
async function start() {
const db = await initMongo()
// minio
const minio = await initMinIO()
app.set('view engine', 'pug')
app.set('views', path.join(__dirname, 'views'))
app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, '/images')))
app.get('/', async (req, res) => {
res.render('index', { notes: await retrieveNotes(db) })
})
app.post(
'/note',
// minio
multer({ storage: multer.memoryStorage() }).single('image'),
async (req, res) => {
if (!req.body.upload && req.body.description) {
await saveNote(db, { description: req.body.description })
res.redirect('/')
} else if (req.body.upload && req.file) {
// minio
await minio.putObject(
minioBucket,
req.file.originalname,
req.file.buffer,
)
// minio
const link = `/img/${encodeURIComponent(req.file.originalname)}`
res.render('index', {
content: `${req.body.description} ![](${link})`,
notes: await retrieveNotes(db),
})
}
},
)
// minio
app.get('/img/:name', async (req, res) => {
const stream = await minio.getObject(
minioBucket,
decodeURIComponent(req.params.name),
)
stream.pipe(res)
})
app.listen(port, () => {
console.log(`App listening on http://localhost:${port}`)
})
}
async function saveNote(db, note) {
await db.insertOne(note)
}
async function retrieveNotes(db) {
const notes = await db.find().toArray()
const sortedNotes = notes.reverse()
return sortedNotes.map(it => ({ ...it, description: marked(it.description) }))
}
start()
===========================================================================================================================
% nano views/index.pug
===========================================================================================================================
html
head
title= title
link(rel="stylesheet", href="tachyons.min.css")
link(rel="stylesheet", href="custom.style.css")
body.ph3.pt0.pb4.mw7.center.sans-serif
h1.f2.mb0 #[img(src='/images/Node.png')] #[span.green N]ode #[img(src='/images/MongoDB.png')] #[span.green M]ongoDB #[img(src='/images/Minio.png')] #[span.brick-red Minio]
<!-- img(src="https://avatars.githubusercontent.com/u/9950313?s=48&v=4") -->
// img(src='https://avatars.githubusercontent.com/u/45120?s=48&v=4')
// img(src='https://avatars.githubusercontent.com/u/695951?s=48&v=4')
p.f5.mt1.mb4.lh-copy A Node.js-MongoDB-MinIO app.
form(action="/note" method="POST" enctype="multipart/form-data")
ol.list.pl0
li.mv3
label.f6.b.db.mb2(for="image") Upload an image
input.f6.link.dim.br1.ba.b--black-20.ph3.pv2.mb2.dib.black.bg-white.pointer(type="file" name="image")
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer.ml2(type="submit" value="Upload" name="upload")
li.mv3
label.f6.b.db.mb2(for="description") Write your content here
textarea.f4.db.border-box.hover-black.w-100.measure.ba.b--black-20.pa2.br2.mb2(rows="5" name="description") #{content || ''}
input.f6.link.dim.br1.ba.bw1.ph3.pv2.mb2.dib.black.bg-white.pointer(type="submit" value="Publish" name="publish")
if notes.length > 0
ul.list.pl0
p.f6.b.db.mb2 Notes
each note in notes
li.mv3.bb.bw2.b--light-blue.bg-washed-blue.ph4.pv2
p.measure!= note.description
else
p.lh-copy.f6 No notes!
===========================================================================================================================
% curl -L https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css
% wget https://raw.githubusercontent.com/tachyons-css/tachyons/master/css/tachyons.min.css --directory-prefix=public
% cat public/tachyons.min.css
% curl --output Node.png "https://avatars.githubusercontent.com/u/9950313?s=48&v=4" --create-dirs --output-dir public/images
% ls public/images/Node.png
% curl --output MongoDB.png "https://avatars.githubusercontent.com/u/45120?s=48&v=4" --create-dirs --output-dir public/images
% ls public/images/MongoDB.png
% curl --output Minio.png "https://avatars.githubusercontent.com/u/695951?s=48&v=4" --create-dirs --output-dir public/images
% ls public/images/Minio.png
===========================================================================================================================
[
% rm -rf ~/Minio
]
% minio server --address 127.0.0.1:9000 --console-address :9090 ~/Minio
% brew services start mongodb-community
% brew services stop mongodb-community
% brew services restart mongodb-community
% mongosh
> db.version()
> show dbs
> use dev
> show collections
[
> db.notes.find()
]
> db.dropDatabase()
> use test
> show dbs
> exit
% node --version
% npm --version
% npm list --global
% npm update --global
% npm list --global
% npm init
# package name:
node-mongodb-app-macos-arm64-v2
# description
A Node.js-MongoDB-MinIO app.
# keywords
Node.js, MongoDB, MinIO
# author:
RajaniCode
# Is this OK? (yes)
yes
% npm install express
% npm install mongodb
% npm install minio
% npm install marked
% npm install multer
% npm install pug
% npm list
% npm update
% npm list
% node index.js
<cotrol + C>
% nano Dockerfile
[
FROM node:23.6.1-slim
COPY . .
RUN npm install
CMD [ "node", "index.js" ]
]
% cat Dockerfile
% echo node_modules > .dockerignore
% cat .dockerignore
% podman build -t node-mongodb-app-macos-arm64-v2 .
% podman container list
% podman image list --all
% podman tag localhost/node-mongodb-app-macos-arm64-v2 ghcr.io/rajanicode/node-mongodb-app:version2.0.0
% podman image list --all
# https://github.com/settings/tokens
# Token Till Focus
% export CR_PAT=****************************************
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin
[
Login Succeeded!
]
% podman push ghcr.io/rajanicode/node-mongodb-app:version2.0.0
[
Writing manifest to image destination
]
https://github.com/users/RajaniCode/packages/container/package/node-mongodb-app
===========================================================================================================================
***************************************************************************************************************************
# https://github.com/settings/tokens
***************************************************************************************************************************
# New personal access token (classic)
# Personal access tokens (classic) function like ordinary OAuth access tokens
# They can be used instead of a password for Git over HTTPS, or can be used to authenticate to the API over Basic Authentication.
# Note
<What’s this token for?>
# Expiration
<dd/mm/yyyy>
# Select scopes
<write:packagesUpload packages to GitHub Package Registry>
<read:packagesDownload packages from GitHub Package Registry>
<delete:packagesDelete packages from GitHub Package Registry>
***************************************************************************************************************************
# Podman # run
***************************************************************************************************************************
% podman --version
% podman version
% podman info
[
% podman machine init
[
% podman system connection list
]
% podman machine start
]
% podman container list --all
% podman image list --all
# https://github.com/settings/tokens
# Token Till Focus
% export CR_PAT=****************************************
% echo $CR_PAT | podman login ghcr.io -u RajaniCode --password-stdin
[
Login Succeeded!
]
# MinIO
% podman network create node-mongodb-app-network
% podman network list
% podman run \
-p 9000:9000 \
-p 9090:9090 \
--name minio \
--network=node-mongodb-app-network \
-v Minio:/data \
-e "MINIO_ACCESS_KEY=minioadmin" \
-e "MINIO_SECRET_KEY=minioadmin" \
ghcr.io/rajanicode/minio:version1.0.0 server /data --console-address ":9090"
# MongoDB
# Terminal New Window # podman run ghcr.io rajanicode mongo version1.0.0
% podman network list
% podman run \
-p 27017:27017 \
--name=mongo \
--rm \
--network=node-mongodb-app-network \
ghcr.io/rajanicode/mongo:version1.0.0
# node-mongodb-app:version1.0.0
# Terminal New Window
% podman network list
% podman run \
--name=node-mongodb-app-v1 \
--rm \
--network=node-mongodb-app-network \
-p 3001:3001 \
-e PORT=3001 \
-e MONGO_URL=mongodb://mongo:27017/dev \
ghcr.io/rajanicode/node-mongodb-app:version1.0.0
# node-mongodb-app:version2.0.0
# Terminal New Window
% podman network list
% podman run \
--name=node-mongodb-app-v2 \
--rm \
--network=node-mongodb-app-network \
-p 3002:3002 \
-e PORT=3002 \
-e MONGO_URL=mongodb://mongo:27017/dev \
-e "MINIO_ACCESS_KEY=minioadmin" \
-e "MINIO_SECRET_KEY=minioadmin" \
-e "MINIO_HOST=minio" \
ghcr.io/rajanicode/node-mongodb-app:version2.0.0
# mongosh # docker
# Terminal New Window
% podman exec -it mongo bash
$ cat /etc/os-release
$ arch
$ apt --version
$ apt-get --version
$ mongosh --version
$ mongosh
> db.version()
> show dbs
> use dev
> show collections
> db.notes.find()
[
> db.dropDatabase()
> use test
> show dbs
]
> exit
podman machine init podman machine start
# mongosh # local machine
# Terminal New Window
% mongosh --version
% brew services start mongodb-community
% brew services stop mongodb-community
% brew services restart mongodb-community
> db.version()
> show dbs
> use dev
> show collections
> db.notes.find()
[
> db.dropDatabase()
> use test
> show dbs
]
> exit
# mc # docker
# Terminal New Window
% podman exec -it minio bash
# cat /etc/os-release
# arch
# ls /etc
# ls /usr/bin
# mc --version
# mc ls data
# mc ls data/minio-bucket
# mc # local machine
# mc alias set # mc get
# Terminal New Window
% mc --version
# % mc alias set minio-podman-localhost "http://127.0.0.1:9000/" "minioadmin" "minioadmin"
% mc alias set minio-podman-docker "http://127.0.0.1:9000/" "minioadmin" "minioadmin"
% mc get minio-podman-docker/minio-bucket/logo_circle_podmandesktop.png $HOME/Desktop/DynamicFolder/new_name_podmandesktop.png
% mc get minio-podman-docker/minio-bucket/podman.io.png $HOME/Desktop/DynamicFolder
% mc get "minio-podman-docker/minio-bucket/GitHub Actions.png" "$HOME/Desktop/DynamicFolder/GitHub Actions.png"
***************************************************************************************************************************
# Podman # Cleanup
***************************************************************************************************************************
% podman system connection list
% podman machine list
% podman container list
% podman image list --all
[
% podman ps --all --quiet
% podman stop $(podman ps -a -q)
% podman rm $(podman ps -a -q) --force
]
% podman container list --all --quiet
% podman stop $(podman container list -a -q)
% podman rm $(podman container list -a -q) --force
% podman container prune
% podman image list --all --quiet
% podman rmi $(podman image list -a -q) --force
% podman image prune --all
% podman volume list --quiet
% podman volume rm $(podman volume list --quiet) --force
% podman volume prune
% podman network ls
% podman network ls --quiet --filter dangling=true
% podman network rm $(podman network ls --quiet --filter dangling=true)
% podman network prune
% podman system info
% podman system prune --all --volumes
% podman system connection list
% podman machine list
% podman machine stop
% podman machine rm podman-machine-default
***************************************************************************************************************************
# Podman
**************************************************************************************************************************
% brew install podman
[
% brew uninstall podman
% ls /opt
% ls /opt/podman
% ls /opt/podman/bin
% ls /opt/podman/bin/podman
% sudo rm -rf /opt/podman
% ls /etc/paths.d
% sudo rm -rf /etc/paths.d/podman-pkg
% ls ~/.local
% rm -rf ~/.local
% ls ~/.config
% rm -rf ~/.config
]
***************************************************************************************************************************
###########################################################################################################################
// Credits
/*
https://podman.io/
https://nodejs.org/
https://npmjs.com/
https://expressjs.com/
https://mongodb.com/
https://min.io/
https://ghcr.io/
https://quay.io/
https://docker.com/
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment