diff --git a/README.md b/README.md
index 25388ec..325858a 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,7 @@ There are some config settings you need to change in the files below.
 | --------- | ------ | ----------- |
 | `NODE_ENV`  | `production` or `development` | set current environment (will apply corresponding settings in the `config.json`) |
 | `DEBUG` | `true` or `false` | set debug mode; show more logs |
+| `CMD_CONFIG_FILE` | `/path/to/config.json` | optional override for the path to CodiMD's config file |
 | `CMD_DOMAIN` | `codimd.org` | domain name |
 | `CMD_URL_PATH` | `codimd` | sub URL path, like `www.example.com/<URL_PATH>` |
 | `CMD_HOST` | `localhost` | host to listen on |
@@ -285,19 +286,15 @@ There are some config settings you need to change in the files below.
 | `defaultPermission` | `freely`, `editable`, `limited`, `locked`, `protected` or `private` | set notes default permission (only applied on signed users) |
 | `dbURL` | `mysql://localhost:3306/database` | set the db URL; if set, then db config (below) won't be applied |
 | `db` | `{ "dialect": "sqlite", "storage": "./db.codimd.sqlite" }` | set the db configs, [see more here](http://sequelize.readthedocs.org/en/latest/api/sequelize/) |
-| `sslKeyPath` | `./cert/client.key` | SSL key path (only need when you set `useSSL`) |
-| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path (only need when you set `useSSL`) |
-| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain (only need when you set `useSSL`) |
-| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path (only need when you set `useSSL`) |
-| `tmpPath` | `./tmp/` | temp directory path |
-| `defaultNotePath` | `./public/default.md` | default note file path |
-| `docsPath` | `./public/docs` | docs directory path |
-| `indexPath` | `./public/views/index.ejs` | index template file path |
-| `hackmdPath` | `./public/views/hackmd.ejs` | hackmd template file path |
-| `errorPath` | `./public/views/error.ejs` | error template file path |
-| `prettyPath` | `./public/views/pretty.ejs` | pretty template file path |
-| `slidePath` | `./public/views/slide.hbs` | slide template file path |
-| `uploadsPath` | `./public/uploads` | uploads directory - needs to be persistent when you use imageUploadType `filesystem` |
+| `sslKeyPath` | `./cert/client.key` | SSL key path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain<sup>1</sup> (only need when you set `useSSL`) |
+| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path<sup>1</sup> (only need when you set `useSSL`) |
+| `tmpPath` | `./tmp/` | temp directory path<sup>1</sup> |
+| `defaultNotePath` | `./public/default.md` | default note file path<sup>1</sup> |
+| `docsPath` | `./public/docs` | docs directory path<sup>1</sup> |
+| `viewPath` | `./public/views` | template directory path<sup>1</sup> |
+| `uploadsPath` | `./public/uploads` | uploads directory<sup>1</sup> - needs to be persistent when you use imageUploadType `filesystem` |
 | `sessionName` | `connect.sid` | cookie session name |
 | `sessionSecret` | `secret` | cookie session secret |
 | `sessionLife` | `14 * 24 * 60 * 60 * 1000` | cookie session life |
@@ -314,6 +311,8 @@ There are some config settings you need to change in the files below.
 | `s3` | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION" }` | When `imageuploadtype` be set to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
 | `s3bucket` | `YOUR_S3_BUCKET_NAME` | bucket name when `imageUploadType` is set to `s3` or `minio` |
 
+<sup>1</sup>: relative paths are based on CodiMD's base directory
+
 ## Third-party integration API key settings
 
 | service | settings location | description |
diff --git a/app.js b/app.js
index 34890bb..46fd041 100644
--- a/app.js
+++ b/app.js
@@ -126,6 +126,9 @@ app.use(i18n.init)
 // routes without sessions
 // static files
 app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime }))
+app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime }))
+app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime }))
+app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime }))
 
 // session
 app.use(session({
@@ -167,7 +170,7 @@ app.use(require('./lib/web/middleware/codiMDVersion'))
 
 // routes need sessions
 // template files
-app.set('views', path.join(__dirname, '/public/views'))
+app.set('views', config.viewPath)
 // set render engine
 app.engine('ejs', ejs.renderFile)
 // set view engine
diff --git a/lib/config/default.js b/lib/config/default.js
index c34279b..cf7d2fc 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -38,15 +38,10 @@ module.exports = {
   sslCAPath: '',
   dhParamPath: '',
   // other path
+  viewPath: './public/views',
   tmpPath: './tmp',
   defaultNotePath: './public/default.md',
   docsPath: './public/docs',
-  indexPath: './public/views/index.ejs',
-  codimdPath: './public/views/codimd.ejs',
-  errorPath: './public/views/error.ejs',
-  prettyPath: './public/views/pretty.ejs',
-  slidePath: './public/views/slide.ejs',
-  constantsPath: './public/js/lib/common/constant.ejs',
   uploadsPath: './public/uploads',
   // session
   sessionName: 'connect.sid',
diff --git a/lib/config/index.js b/lib/config/index.js
index 26f0ae9..7d059c5 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -9,7 +9,7 @@ const deepFreeze = require('deep-freeze')
 const {Environment, Permission} = require('./enum')
 const logger = require('../logger')
 
-const appRootPath = path.join(__dirname, '../../')
+const appRootPath = path.resolve(__dirname, '../../')
 const env = process.env.NODE_ENV || Environment.development
 const debugConfig = {
   debug: (env === Environment.development)
@@ -23,7 +23,8 @@ const packageConfig = {
   minimumCompatibleVersion: '0.5.0'
 }
 
-const configFilePath = path.join(appRootPath, 'config.json')
+const configFilePath = path.resolve(appRootPath, process.env.CMD_CONFIG_FILE ||
+'config.json')
 const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined
 
 let config = require('./default')
@@ -173,20 +174,14 @@ config.sslCAPath.forEach(function (capath, i, array) {
   array[i] = path.resolve(appRootPath, capath)
 })
 
-config.sslCertPath = path.join(appRootPath, config.sslCertPath)
-config.sslKeyPath = path.join(appRootPath, config.sslKeyPath)
-config.dhParamPath = path.join(appRootPath, config.dhParamPath)
-
-config.tmpPath = path.join(appRootPath, config.tmpPath)
-config.defaultNotePath = path.join(appRootPath, config.defaultNotePath)
-config.docsPath = path.join(appRootPath, config.docsPath)
-config.indexPath = path.join(appRootPath, config.indexPath)
-config.codimdPath = path.join(appRootPath, config.codimdPath)
-config.errorPath = path.join(appRootPath, config.errorPath)
-config.prettyPath = path.join(appRootPath, config.prettyPath)
-config.slidePath = path.join(appRootPath, config.slidePath)
-config.constantsPath = path.join(appRootPath, config.constantsPath)
-config.uploadsPath = path.join(appRootPath, config.uploadsPath)
+config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
+config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
+config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
+config.viewPath = path.resolve(appRootPath, config.viewPath)
+config.tmpPath = path.resolve(appRootPath, config.tmpPath)
+config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath)
+config.docsPath = path.resolve(appRootPath, config.docsPath)
+config.uploadsPath = path.resolve(appRootPath, config.uploadsPath)
 
 // make config readonly
 config = deepFreeze(config)
diff --git a/lib/models/revision.js b/lib/models/revision.js
index 8bc95cb..4ee080d 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -5,6 +5,7 @@ var async = require('async')
 var moment = require('moment')
 var childProcess = require('child_process')
 var shortId = require('shortid')
+var path = require('path')
 
 // core
 var config = require('../config')
@@ -14,7 +15,7 @@ var dmpWorker = createDmpWorker()
 var dmpCallbackCache = {}
 
 function createDmpWorker () {
-  var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
+  var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
     stdio: 'ignore'
   })
   if (config.debug) logger.info('dmp worker process started')
diff --git a/lib/response.js b/lib/response.js
index 4df036b..d30f2a8 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -54,7 +54,7 @@ var response = {
 }
 
 function responseError (res, code, detail, msg) {
-  res.status(code).render(config.errorPath, {
+  res.status(code).render('error.ejs', {
     url: config.serverURL,
     title: code + ' ' + detail + ' ' + msg,
     code: code,
@@ -104,11 +104,11 @@ function showIndex (req, res, next) {
     }).then(function (user) {
       if (user) {
         data.deleteToken = user.deleteToken
-        res.render(config.indexPath, data)
+        res.render('index.ejs', data)
       }
     })
   } else {
-    res.render(config.indexPath, data)
+    res.render('index.ejs', data)
   }
 }
 
@@ -122,7 +122,7 @@ function responseCodiMD (res, note) {
     'Cache-Control': 'private', // only cache by client
     'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
   })
-  res.render(config.codimdPath, {
+  res.render('codimd.ejs', {
     url: config.serverURL,
     title: title,
     useCDN: config.useCDN,
@@ -283,7 +283,7 @@ function renderPublish (data, res) {
   res.set({
     'Cache-Control': 'private' // only cache by client
   })
-  res.render(config.prettyPath, data)
+  res.render('pretty.ejs', data)
 }
 
 function actionPublish (req, res, note) {
@@ -665,7 +665,7 @@ function renderPublishSlide (data, res) {
   res.set({
     'Cache-Control': 'private' // only cache by client
   })
-  res.render(config.slidePath, data)
+  res.render('slide.ejs', data)
 }
 
 module.exports = response
diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js
index 4bf82b3..8c432b0 100644
--- a/lib/web/imageRouter/filesystem.js
+++ b/lib/web/imageRouter/filesystem.js
@@ -1,5 +1,6 @@
 'use strict'
 const url = require('url')
+const path = require('path')
 
 const config = require('../../config')
 const logger = require('../../logger')
@@ -15,5 +16,5 @@ exports.uploadImage = function (imagePath, callback) {
     return
   }
 
-  callback(null, url.resolve(config.serverURL + '/', imagePath.match(/public\/(.+)$/)[1]))
+  callback(null, url.resolve(config.serverURL + '/uploads/', path.basename(imagePath)))
 }
diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js
index 7ecf383..fb2609e 100644
--- a/lib/web/statusRouter.js
+++ b/lib/web/statusRouter.js
@@ -105,5 +105,5 @@ statusRouter.get('/config', function (req, res) {
     'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
     'Content-Type': 'application/javascript'
   })
-  res.render(config.constantsPath, data)
+  res.render('../js/lib/common/constant.ejs', data)
 })